Project import
diff --git a/faux-folly/Android.mk b/faux-folly/Android.mk
new file mode 100644
index 0000000..ab119b0
--- /dev/null
+++ b/faux-folly/Android.mk
@@ -0,0 +1,3 @@
+LOCAL_PATH := $(my-dir)
+
+include $(LOCAL_PATH)/folly/Android.mk
diff --git a/faux-folly/LICENSE b/faux-folly/LICENSE
new file mode 100644
index 0000000..f433b1a
--- /dev/null
+++ b/faux-folly/LICENSE
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/faux-folly/MODULE_LICENSE_APACHE b/faux-folly/MODULE_LICENSE_APACHE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/faux-folly/MODULE_LICENSE_APACHE
diff --git a/faux-folly/README.faux-folly.md b/faux-folly/README.faux-folly.md
new file mode 100644
index 0000000..05e6d6c
--- /dev/null
+++ b/faux-folly/README.faux-folly.md
@@ -0,0 +1,207 @@
+The Faux Folly Manifest: Intent and Implementation
+--------------------------------------------------
+
+Folly is an open-source C++ library developed and used at Facebook.
+
+Faux-Folly is a fork of Folly that removes everything that is not
+required by the Futures library in Folly. It was created by the Nest
+Camera Software team for use in Camera products.
+
+Why?
+----
+
+Folly includes a state-of-the-art C++ Futures/Promises library that is
+thread-friendly and does not impose the use of coroutines. This opens
+up better, less-error-prone programming styles when doing asynchronous
+programming. I won't go into them in this doc, but instead refer you
+to the Nest presentation "TDD-lite, Futures, and Promises:"
+
+https://docs.google.com/a/nestlabs.com/presentation/d/1rKi_LhuY26zgzLRREwdkrDmp_qyTdkUfBO2IZvorK_A/edit?usp=sharing
+
+When porting Folly to Brillo/Android, we ran in to numerous problems:
+
+    1. The only thing we really want (or need) from Folly is the
+       futures library. Folly is a very broad, boost-like library full
+       of Good Ideas and Bad Ideas and Different Ideas. Developers
+       will constantly try to use the more dubious constructs in the
+       library (e.g. their spin-lock implementation)... and we
+       discourage that.
+
+    2. Brillo/Android uses BoringSSL instead of OpenSSL, and we found
+       it too hard to port the code. BoringSSL is a Google fork of
+       OpenSSL, and is neither ABI nor API compatible with
+       OpenSSL. However, it uses the <openssl/*> header namespace and
+       outputs libraries libssl.so and libcrypt.so. In other words,
+       they intentionally make it hard for anyone to have BoringSSL
+       and OpenSSL co-existing on the system. Since we don't have no
+       plans to use the SSL-parts of the library, it makes more sense
+       to remove it.
+
+    3. Lots of dependencies. The original dependency list of Folly is
+       a little long, and most of those things have not been ported to
+       Android/Brillo, and most of them are not related to the Futures
+       library. Why ship them on device??
+
+    4. We keep running into problems with Folly because we're
+       violating the assumption that this is an x86_64 server
+       application. In one case, the CacheLocality class unloaded
+       linux-vdso.so.1 because it couldn't find an x86-specific
+       symbol. This breaks things like clock_gettime. Since our team
+       will need to support this library with little or no interaction
+       with Facebook, it needs a smaller surface area to support.
+
+And because of these, we chose to fork the library and trim away
+everything that wasn't part of the futures library.  All said and
+done, the fork removes over 115,000 LOC.
+
+So... why not use boost::future<> or std::future<>?
+
+std::future<>: as of C++14, there is still no support for asynchronous
+programming styles/workflows. Namely: they do not support
+continuations. See
+https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Herb-Sutter-Concurrency-and-Parallelism .
+Folly Futures supports this.
+
+boost::future<>: The latest boost thread library does indeed include
+support for asynchronous programming styles (continuations), but it
+the destructor of boost::future<> will call wait() if the future is
+not realized. Put another way: you can not orphan (forget) a
+future. I.e. you can't do this:
+
+```
+void Foo::bar(SomeObject& o)
+{
+    boost::future<int> f = o.get_value();
+    f.then([=](int val) {
+        scoped_lock lock(this->m_mutex);
+        this->m_val = val;
+    });
+}
+```
+
+The continuation (the .then() lambda) has everything. There's no need
+to keep the future, we just need to make sure that the continuation
+fires.
+
+So, with boost this means that you must create some kind of
+garbage-collection mechanism that will hold all of the unrealized
+futures and periodically clean them up as futures are realized. That's
+a lot of bookkeeping, and the purpose of using futures<> is to
+/reduce/ explicit bookkeeping. Folly Futures /does/ support this
+style, though it has a few gaps for doing it "right."
+
+What are the gaps in Folly Futures for orphaning the future?
+
+    1. There is no concept of a compulsary cancellation.  Once I set
+       the continuation... you must ensure that all references stay
+       valid until the continuation fires.  Again... more
+       bookkeeping. However, it's fairly simple to add a cancellation
+       concept to the library.
+
+    2. If the future is set to an exception state (i.e. an error), you
+       must either provide an .onError() callback every time you
+       create a .then() callback, or you will not get a notification
+       of the error. This one is trickier to solve without breaking
+       "normal" (non-orphaning) futures use-cases, but we have found a
+       solution where you can designate an executor on which the
+       exception should fire.
+
+So... why not write our own Futures library? Doesn't Folly do a lot of
+strange things with templates and move semantics that are hard to
+understand?
+
+As we worked with several libraries that implement futures and
+promises, we found that writing a high-quality, high-reliability
+futures library requires a lot of effort and thought to design it well
+and implement it well. All of the implementations do strange tricks
+with templates and move semantics. All of the implementations, too,
+have solution to corner-cases like promise<void> and shared
+futures. They also include libraries for aggregating futures
+(e.g. functions that take a list of futures and returns a new future
+that is realized when any or all of the futures in the list have a
+value or error). Some of the code we've found that is hard to
+understand was added because there was a valid use-case that wasn't
+working or because there was a deadlock or race condition.
+
+And if we were to write our own, it would end up having almost the
+same API and implementation as Folly... because it is designed well.
+
+How?
+----
+
+The basic process was something along the lines of:
+
+   1. Delete something not in folly/futures/ directory.
+   2. Compile.
+   3. If compile succeeds: run unit tests.
+   4. If unit tests pass: commit!
+   5. If compile or tests fail: skip. (don't delete it)
+
+Once that was done, there were a few libraries that we found to be
+tangled up:
+
+   experimental/fibers: a coroutines library
+   experimental/io: a whole bunch of socket programming stuff
+
+So, we made two (possibly dubious) replacements so that we could break
+this dependency:
+
+    1. Replaced folly::fibers::Baton<> with folly::Baton<>. This isn't
+       a very clean replacement.
+       
+    2. Remove folly::RequestContext from Futures. This is some kind of
+       system to attach void*-like stuff to future/promises. It came
+       out cleanly.
+
+    3. The implementation of Futures was using folly/io/async/HWWheelTimer.
+       In order to remove the async library we needed a replacement. So I
+       wrote a class to give equivalent functionality (but not using the
+       wheel timer algorithm).
+
+New components also come with new unit tests.
+
+Pulling Changes from Upstream
+-----------------------------
+
+It is possible to pull changes from upstream. We forked folly at
+version 0.52.0. Up until 0.57.0 merges are fairly straightforward and
+work like this:
+
+    0. Start by compiling for your host machine... not Android.
+    
+    1. Do a git merge on the upstream tag. You will get conflicts.
+    
+    2. If it is a file we have deleted, keep it deleted. The `git
+       status' will say something like "deleted in ours."
+
+    3. If it is a new file in a library we have deleted
+       (experimental/* wangle/*, gen/*, io/async/*) then delete it.
+
+    4. If it is a Makefile, you will need to figure out what is
+       added on "our" branch vs. what is added on "their" branch.
+       Usually this is as simple as ignoring the upstream change
+       if they simply added files that we delete.
+
+    5. For any other kind of conflict, you'll need to put your
+       engineer's hat on and work it out.
+
+    6. Build the code. Ideally, there should be no warnings.
+
+    7. Run all the unit tests. They should all pass.
+
+    8. As much as possible do only the minimum needed in order to
+       get the code to compile and tests to pass. If you run into
+       a large problem... it is better to break up the changes.
+       Get the merge commit just so that it builds, and then resolve
+       issues on subsequent commits on a mini-branch.
+
+In version 0.57.0, we run in to trouble because of our replacement of
+Baton<> in Futures doesn't work as expected for some new test cases.
+
+What's Next?
+------------
+
+We might rename the .so-file so that this library can co-exist with
+folly on the same system.
+
+There's probably no need to rename the folly:: namespace.
diff --git a/faux-folly/README.md b/faux-folly/README.md
new file mode 100644
index 0000000..244fc41
--- /dev/null
+++ b/faux-folly/README.md
@@ -0,0 +1,153 @@
+Folly: Facebook Open-source Library
+-----------------------------------
+
+Folly is an open-source C++ library developed and used at Facebook.
+
+###[Get Started](folly/docs/Overview.md)
+
+Folly is published on Github at https://github.com/facebook/folly; for
+discussions, there is a Google group at
+https://groups.google.com/d/forum/facebook-folly.
+
+Dependencies
+------------
+
+folly requires gcc 4.8+ and a version of boost compiled with C++11 support.
+
+Please download googletest from
+https://googletest.googlecode.com/files/gtest-1.7.0.zip and unzip it in the
+folly/test subdirectory.
+
+Ubuntu 12.04
+------------
+
+This release is old, requiring many upgrades. However, since Travis CI runs
+on 12.04, `folly/build/deps_ubuntu_12.04.sh` is provided, and upgrades all
+the required packages.
+
+Ubuntu 13.10
+------------
+
+The following packages are required (feel free to cut and paste the apt-get
+command below):
+
+```
+sudo apt-get install \
+    g++ \
+    automake \
+    autoconf \
+    autoconf-archive \
+    libtool \
+    libboost-all-dev \
+    libevent-dev \
+    libdouble-conversion-dev \
+    libgoogle-glog-dev \
+    libgflags-dev \
+    make \
+    binutils-dev \
+    libjemalloc-dev \
+    libssl-dev
+```
+
+Ubuntu 14.04 LTS
+----------------
+
+The packages listed above for Ubuntu 13.10 are required, as well as:
+
+```
+sudo apt-get install \
+    libiberty-dev
+```
+
+The above packages are sufficient for Ubuntu 13.10 and Ubuntu 14.04.
+
+In the folly directory, run
+```
+  autoreconf -ivf
+  ./configure
+  make
+  make check
+  sudo make install
+```
+
+OS X (Homebrew)
+----
+folly is available as a Formula and releases may be built via `brew install folly`.
+
+You may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `master`:
+
+```
+  cd folly
+  ./build/bootstrap-osx-homebrew.sh
+  make
+  make check
+```
+
+OS X (MacPorts)
+----
+Install the required packages from MacPorts:
+
+```
+  sudo port install \
+    autoconf \
+    automake \
+    boost \
+    gflags \
+    git \
+    google-glog \
+    libevent \
+    libtool \
+    scons \
+```
+
+Download and install double-conversion:
+
+```
+  git clone https://github.com/google/double-conversion.git
+  cd double-conversion
+  cmake -DBUILD_SHARED_LIBS=ON .
+  make
+  sudo make install
+```
+
+Download and install folly with the parameters listed below:
+
+```
+  git clone https://github.com/facebook/folly.git
+  cd folly/folly
+  autoreconf -ivf
+  ./configure CPPFLAGS="-I/opt/local/include" LDFLAGS="-L/opt/local/lib"
+  make
+  sudo make install
+```
+
+Other Linux distributions
+-------------------------
+
+- double-conversion (https://github.com/google/double-conversion)
+
+  Download and build double-conversion.
+  You may need to tell configure where to find it.
+
+  [double-conversion/] `ln -s src double-conversion`
+
+  [folly/] `./configure LDFLAGS=-L$DOUBLE_CONVERSION_HOME/ CPPFLAGS=-I$DOUBLE_CONVERSION_HOME/`
+
+  [folly/] `LD_LIBRARY_PATH=$DOUBLE_CONVERSION_HOME/ make`
+
+- additional platform specific dependencies:
+
+  Fedora 21 64-bit
+    - gcc
+    - gcc-c++
+    - autoconf
+    - autoconf-archive
+    - automake
+    - boost-devel
+    - libtool
+    - glog-devel
+    - gflags-devel
+    - scons
+    - double-conversion-devel
+    - openssl-devel
+    - libevent-devel
diff --git a/faux-folly/folly/Android.mk b/faux-folly/folly/Android.mk
new file mode 100644
index 0000000..4357fac
--- /dev/null
+++ b/faux-folly/folly/Android.mk
@@ -0,0 +1,262 @@
+LOCAL_PATH := $(my-dir)
+
+FOLLY_LIBFOLLYBASE_SOURCES := \
+	Conv.cpp \
+	Demangle.cpp \
+	android/src/EscapeTables.cpp \
+	Format.cpp \
+	android/src/FormatTables.cpp \
+	Malloc.cpp \
+	Range.cpp \
+	StringBase.cpp \
+	String.cpp \
+	detail/FunctionalExcept.cpp
+
+FOLLY_LIBFOLLY_SOURCES = \
+	detail/CacheLocality.cpp \
+	dynamic.cpp \
+	File.cpp \
+	FileUtil.cpp \
+	futures/detail/ThreadWheelTimekeeper.cpp \
+	futures/detail/TimerMap.cpp \
+	futures/Barrier.cpp \
+	futures/ThreadedExecutor.cpp \
+	futures/Future.cpp \
+	futures/InlineExecutor.cpp \
+	futures/ManualExecutor.cpp \
+	futures/QueuedImmediateExecutor.cpp \
+	futures/ScheduledExecutor.cpp \
+	detail/Futex.cpp \
+	LifoSem.cpp \
+	io/IOBuf.cpp \
+	io/IOBufQueue.cpp \
+	detail/MemoryIdler.cpp \
+	Random.cpp \
+	SpookyHashV1.cpp \
+	SpookyHashV2.cpp \
+	stats/Instantiations.cpp \
+	Version.cpp
+
+###
+### libfollybase
+###
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android/build/faux-folly-common.mk
+LOCAL_SRC_FILES := \
+	$(FOLLY_LIBFOLLYBASE_SOURCES)
+LOCAL_MODULE := libfollybase
+include $(BUILD_STATIC_LIBRARY)
+
+###
+### libfolly
+###
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android/build/faux-folly-common.mk
+# See https://groups.google.com/forum/#!topic/android-ndk/6TR4MA7LxYg
+# the android defaults triggers the problem at CacheLocality.cpp:235
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections
+LOCAL_SRC_FILES := \
+	$(FOLLY_LIBFOLLY_SOURCES)
+LOCAL_WHOLE_STATIC_LIBRARIES += libfollybase
+LOCAL_STATIC_LIBRARIES += libjemalloc
+LOCAL_MODULE := libfolly
+include $(BUILD_SHARED_LIBRARY)
+
+###
+### libfollybenchmark
+###
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android/build/faux-folly-common.mk
+LOCAL_SRC_FILES := Benchmark.cpp
+LOCAL_SHARED_LIBRARIES += libboost_regex \
+	libfolly
+LOCAL_MODULE := libfollybenchmark
+include $(BUILD_STATIC_LIBRARY)
+
+###
+### UNIT TESTS
+###
+
+FAUX_FOLLY_UNIT_TESTS :=
+FAUX_FOLLY_BUILD_TEST_EXECUTABLE := $(LOCAL_PATH)/android/build/faux-folly-test-case.mk
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/ForeachTest.cpp
+LOCAL_MODULE := foreach_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/HashTest.cpp
+LOCAL_MODULE := hash_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/FBStringTest.cpp
+LOCAL_MODULE := fbstring_test_using_jemalloc
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/ThreadCachedIntTest.cpp
+LOCAL_MODULE := thread_cached_int_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/ThreadLocalTest.cpp
+LOCAL_SHARED_LIBRARIES += libboost_thread \
+	libboost_system
+LOCAL_MODULE := thread_local_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/FBVectorTest.cpp
+LOCAL_MODULE := fbvector_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+## # fails due to cout
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/DynamicTest.cpp
+LOCAL_MODULE := dynamic_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/BenchmarkTest.cpp
+LOCAL_MODULE := benchmark_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+# fails due to destructor
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/ScopeGuardTest.cpp
+LOCAL_MODULE := scope_guard_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/ConvTest.cpp
+LOCAL_MODULE := conv_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/RangeTest.cpp
+LOCAL_MODULE := range_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/BitsTest.cpp
+LOCAL_MODULE := bits_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/BitIteratorTest.cpp
+LOCAL_MODULE := bit_iterator_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/EndianTest.cpp
+LOCAL_MODULE := endian_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/HistogramTest.cpp
+LOCAL_MODULE := histogram_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/MapUtilTest.cpp
+LOCAL_MODULE := map_util_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/StringTest.cpp
+LOCAL_MODULE := string_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/FormatTest.cpp
+LOCAL_MODULE := format_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/PortabilityTest.cpp
+LOCAL_MODULE := portability_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/SpookyHashV1Test.cpp
+LOCAL_MODULE := spooky_hash_v1_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/SpookyHashV2Test.cpp
+LOCAL_MODULE := spooky_hash_v2_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/CancellationTest.cpp
+LOCAL_MODULE := cancellation_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/MPMCQueueTest.cpp \
+	test/DeterministicSchedule.cpp
+LOCAL_MODULE := mpmc_queue_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/FutexTest.cpp \
+	test/DeterministicSchedule.cpp
+LOCAL_MODULE := futex_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test/DeterministicScheduleTest.cpp \
+	test/DeterministicSchedule.cpp
+LOCAL_MODULE := deterministic_schedule_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+	futures/test/CancellationTest.cpp \
+	futures/test/CollectTest.cpp \
+	futures/test/ThreadedExecutorTest.cpp \
+	futures/test/EnsureTest.cpp \
+	futures/test/ExecutorTest.cpp \
+	futures/test/FSMTest.cpp \
+	futures/test/FilterTest.cpp \
+	futures/test/FutureTest.cpp \
+	futures/test/HeaderCompileTest.cpp \
+	futures/test/InterruptTest.cpp \
+	futures/test/MapTest.cpp \
+	futures/test/PollTest.cpp \
+	futures/test/PromiseTest.cpp \
+	futures/test/ReduceTest.cpp \
+	futures/test/SharedPromiseTest.cpp \
+	futures/test/ThenCompileTest.cpp \
+	futures/test/ThenTest.cpp \
+	futures/test/TimekeeperTest.cpp \
+	futures/test/TimerMapTest.cpp \
+	futures/test/TryTest.cpp \
+	futures/test/UnitTest.cpp \
+	futures/test/UnwrapTest.cpp \
+	futures/test/ViaTest.cpp \
+	futures/test/WaitTest.cpp \
+	futures/test/WillEqualTest.cpp \
+	futures/test/WindowTest.cpp
+LOCAL_SHARED_LIBRARIES += libboost_thread \
+	libboost_system
+LOCAL_MODULE := futures_test
+include $(FAUX_FOLLY_BUILD_TEST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := android/test/faux-folly-tests.sh
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/faux-folly
+LOCAL_REQUIRED_MODULES := $(FAUX_FOLLY_UNIT_TESTS)
+LOCAL_MODULE := faux-folly-tests.sh
+include $(BUILD_PREBUILT)
+
+###
+### Clean the environment...
+### the EPA loves us!
+###
+override FOLLY_LIBFOLLYBASE_SOURCES :=
+override FOLLY_LIBFOLLY_SOURCES :=
diff --git a/faux-folly/folly/ApplyTuple.h b/faux-folly/folly/ApplyTuple.h
new file mode 100644
index 0000000..a592704
--- /dev/null
+++ b/faux-folly/folly/ApplyTuple.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ * Defines a function folly::applyTuple, which takes a function and a
+ * std::tuple of arguments and calls the function with those
+ * arguments.
+ *
+ * Example:
+ *
+ *    int x = folly::applyTuple(std::plus<int>(), std::make_tuple(12, 12));
+ *    ASSERT(x == 24);
+ */
+
+#ifndef FOLLY_APPLYTUPLE_H_
+#define FOLLY_APPLYTUPLE_H_
+
+#include <tuple>
+#include <functional>
+#include <type_traits>
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+namespace detail {
+
+// This is to allow using this with pointers to member functions,
+// where the first argument in the tuple will be the this pointer.
+template<class F> F& makeCallable(F& f) { return f; }
+template<class R, class C, class ...A>
+auto makeCallable(R (C::*d)(A...)) -> decltype(std::mem_fn(d)) {
+  return std::mem_fn(d);
+}
+
+template<class Tuple>
+struct DerefSize
+  : std::tuple_size<typename std::remove_reference<Tuple>::type>
+{};
+
+template<class Tuple, class ...Unpacked> struct ExprDoUnpack {
+  enum {
+    value = sizeof...(Unpacked) < DerefSize<Tuple>::value
+  };
+};
+
+template<class Tuple, class ...Unpacked> struct ExprIsUnpacked {
+  enum {
+    value = sizeof...(Unpacked) == DerefSize<Tuple>::value
+  };
+};
+
+// CallTuple recursively unpacks tuple arguments so we can forward
+// them into the function.
+template<class Ret>
+struct CallTuple {
+  template<class F, class Tuple, class ...Unpacked>
+  static typename std::enable_if<ExprDoUnpack<Tuple, Unpacked...>::value,
+    Ret
+  >::type call(const F& f, Tuple&& t, Unpacked&&... unp) {
+    typedef typename std::tuple_element<
+      sizeof...(Unpacked),
+      typename std::remove_reference<Tuple>::type
+    >::type ElementType;
+    return CallTuple<Ret>::call(f, std::forward<Tuple>(t),
+      std::forward<Unpacked>(unp)...,
+      std::forward<ElementType>(std::get<sizeof...(Unpacked)>(t))
+    );
+  }
+
+  template<class F, class Tuple, class ...Unpacked>
+  static typename std::enable_if<ExprIsUnpacked<Tuple, Unpacked...>::value,
+    Ret
+  >::type call(const F& f, Tuple&& t, Unpacked&&... unp) {
+    return makeCallable(f)(std::forward<Unpacked>(unp)...);
+  }
+};
+
+// The point of this meta function is to extract the contents of the
+// tuple as a parameter pack so we can pass it into std::result_of<>.
+template<class F, class Args> struct ReturnValue;
+template<class F, class ...Args>
+struct ReturnValue<F,std::tuple<Args...>> {
+  typedef typename std::result_of<F (Args...)>::type type;
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+template<class Callable, class Tuple>
+typename detail::ReturnValue<
+  typename std::decay<Callable>::type,
+  typename std::decay<Tuple>::type
+>::type
+applyTuple(const Callable& c, Tuple&& t) {
+  typedef typename detail::ReturnValue<
+    typename std::decay<Callable>::type,
+    typename std::decay<Tuple>::type
+  >::type RetT;
+  return detail::CallTuple<RetT>::call(c, std::forward<Tuple>(t));
+}
+
+//////////////////////////////////////////////////////////////////////
+
+}
+
+#endif
diff --git a/faux-folly/folly/AtomicStruct.h b/faux-folly/folly/AtomicStruct.h
new file mode 100644
index 0000000..ce1dedd
--- /dev/null
+++ b/faux-folly/folly/AtomicStruct.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_ATOMIC_STRUCT_H_
+#define FOLLY_ATOMIC_STRUCT_H_
+
+#include <atomic>
+#include <type_traits>
+#include <folly/Traits.h>
+#include <string.h>
+#include <stdint.h>
+
+namespace folly {
+
+namespace detail {
+template <int N> struct AtomicStructIntPick {};
+}
+
+/// AtomicStruct<T> work like C++ atomics, but can be used on any POD
+/// type <= 8 bytes.
+template <
+    typename T,
+    template<typename> class Atom = std::atomic,
+    typename Raw = typename detail::AtomicStructIntPick<sizeof(T)>::type>
+class AtomicStruct {
+  static_assert(alignof(T) <= alignof(Raw),
+      "target type can't have stricter alignment than matching int");
+  static_assert(sizeof(T) <= sizeof(Raw),
+      "underlying type isn't big enough");
+  static_assert(std::is_trivial<T>::value ||
+                folly::IsTriviallyCopyable<T>::value,
+      "target type must be trivially copyable");
+
+  Atom<Raw> data;
+
+  static Raw encode(T v) noexcept {
+    // we expect the compiler to optimize away the memcpy, but without
+    // it we would violate strict aliasing rules
+    Raw d = 0;
+    memcpy(&d, &v, sizeof(T));
+    return d;
+  }
+
+  static T decode(Raw d) noexcept {
+    T v;
+    memcpy(&v, &d, sizeof(T));
+    return v;
+  }
+
+ public:
+  AtomicStruct() = default;
+  ~AtomicStruct() = default;
+  AtomicStruct(AtomicStruct<T> const &) = delete;
+  AtomicStruct<T>& operator= (AtomicStruct<T> const &) = delete;
+
+  constexpr /* implicit */ AtomicStruct(T v) noexcept : data(encode(v)) {}
+
+  bool is_lock_free() const noexcept {
+    return data.is_lock_free();
+  }
+
+  bool compare_exchange_strong(
+          T& v0, T v1,
+          std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    Raw d0 = encode(v0);
+    bool rv = data.compare_exchange_strong(d0, encode(v1), mo);
+    if (!rv) {
+      v0 = decode(d0);
+    }
+    return rv;
+  }
+
+  bool compare_exchange_weak(
+          T& v0, T v1,
+          std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    Raw d0 = encode(v0);
+    bool rv = data.compare_exchange_weak(d0, encode(v1), mo);
+    if (!rv) {
+      v0 = decode(d0);
+    }
+    return rv;
+  }
+
+  T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    return decode(data.exchange(encode(v), mo));
+  }
+
+  /* implicit */ operator T () const noexcept {
+    return decode(data);
+  }
+
+  T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept {
+    return decode(data.load(mo));
+  }
+
+  T operator= (T v) noexcept {
+    return decode(data = encode(v));
+  }
+
+  void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    data.store(encode(v), mo);
+  }
+
+  // std::atomic also provides volatile versions of all of the access
+  // methods.  These are callable on volatile objects, and also can
+  // theoretically have different implementations than their non-volatile
+  // counterpart.  If someone wants them here they can easily be added
+  // by duplicating the above code and the corresponding unit tests.
+};
+
+namespace detail {
+
+template <> struct AtomicStructIntPick<1> { typedef uint8_t type; };
+template <> struct AtomicStructIntPick<2> { typedef uint16_t type; };
+template <> struct AtomicStructIntPick<3> { typedef uint32_t type; };
+template <> struct AtomicStructIntPick<4> { typedef uint32_t type; };
+template <> struct AtomicStructIntPick<5> { typedef uint64_t type; };
+template <> struct AtomicStructIntPick<6> { typedef uint64_t type; };
+template <> struct AtomicStructIntPick<7> { typedef uint64_t type; };
+template <> struct AtomicStructIntPick<8> { typedef uint64_t type; };
+
+} // namespace detail
+
+} // namespace folly
+
+#endif
diff --git a/faux-folly/folly/Baton.h b/faux-folly/folly/Baton.h
new file mode 100644
index 0000000..6600079
--- /dev/null
+++ b/faux-folly/folly/Baton.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BATON_H
+#define FOLLY_BATON_H
+
+#include <stdint.h>
+#include <atomic>
+#include <boost/noncopyable.hpp>
+#include <errno.h>
+#include <assert.h>
+
+#include <folly/detail/Futex.h>
+#include <folly/detail/MemoryIdler.h>
+
+namespace folly {
+
+/// A Baton allows a thread to block once and be awoken: it captures
+/// a single handoff.  During its lifecycle (from construction/reset to
+/// destruction/reset) a baton must either be post()ed and wait()ed exactly
+/// once each, or not at all.
+///
+/// Baton includes no internal padding, and is only 4 bytes in size.
+/// Any alignment or padding to avoid false sharing is up to the user.
+///
+/// This is basically a stripped-down semaphore that supports only a
+/// single call to sem_post and a single call to sem_wait.  The current
+/// posix semaphore sem_t isn't too bad, but this provides more a bit more
+/// speed, inlining, smaller size, a guarantee that the implementation
+/// won't change, and compatibility with DeterministicSchedule.  By having
+/// a much more restrictive lifecycle we can also add a bunch of assertions
+/// that can help to catch race conditions ahead of time.
+template <template<typename> class Atom = std::atomic>
+struct Baton {
+  Baton() : state_(INIT) {}
+
+  /* using boost::noncopyable increases the object size by 4 bytes */
+  Baton(const Baton&) = delete;
+  Baton& operator=(const Baton&) = delete;
+  Baton(Baton&&) = delete;
+  Baton& operator=(Baton&&) = delete;
+
+  /// It is an error to destroy a Baton on which a thread is currently
+  /// wait()ing.  In practice this means that the waiter usually takes
+  /// responsibility for destroying the Baton.
+  ~Baton() {
+    // The docblock for this function says that it can't be called when
+    // there is a concurrent waiter.  We assume a strong version of this
+    // requirement in which the caller must _know_ that this is true, they
+    // are not allowed to be merely lucky.  If two threads are involved,
+    // the destroying thread must actually have synchronized with the
+    // waiting thread after wait() returned.  To convey causality the the
+    // waiting thread must have used release semantics and the destroying
+    // thread must have used acquire semantics for that communication,
+    // so we are guaranteed to see the post-wait() value of state_,
+    // which cannot be WAITING.
+    //
+    // Note that since we only care about a single memory location,
+    // the only two plausible memory orders here are relaxed and seq_cst.
+    assert(state_.load(std::memory_order_relaxed) != WAITING);
+  }
+
+  /// Equivalent to destroying the Baton and creating a new one.  It is
+  /// a bug to call this while there is a waiting thread, so in practice
+  /// the waiter will be the one that resets the baton.
+  void reset() {
+    // See ~Baton for a discussion about why relaxed is okay here
+    assert(state_.load(std::memory_order_relaxed) != WAITING);
+
+    // We use a similar argument to justify the use of a relaxed store
+    // here.  Since both wait() and post() are required to be called
+    // only once per lifetime, no thread can actually call those methods
+    // correctly after a reset() unless it synchronizes with the thread
+    // that performed the reset().  If a post() or wait() on another thread
+    // didn't synchronize, then regardless of what operation we performed
+    // here there would be a race on proper use of the Baton's spec
+    // (although not on any particular load and store).  Put another way,
+    // we don't need to synchronize here because anybody that might rely
+    // on such synchronization is required by the baton rules to perform
+    // an additional synchronization that has the desired effect anyway.
+    //
+    // There is actually a similar argument to be made about the
+    // constructor, in which the fenceless constructor initialization
+    // of state_ is piggybacked on whatever synchronization mechanism
+    // distributes knowledge of the Baton's existence
+    state_.store(INIT, std::memory_order_relaxed);
+  }
+
+  /// Causes wait() to wake up.  For each lifetime of a Baton (where a
+  /// lifetime starts at construction or reset() and ends at destruction
+  /// or reset()) there can be at most one call to post().  Any thread
+  /// may call post().
+  ///
+  /// Although we could implement a more generic semaphore semantics
+  /// without any extra size or CPU overhead, the single-call limitation
+  /// allows us to have better assert-ions during debug builds.
+  void post() {
+    uint32_t before = state_.load(std::memory_order_acquire);
+
+    assert(before == INIT || before == WAITING || before == TIMED_OUT);
+
+    if (before == INIT &&
+        state_.compare_exchange_strong(before, EARLY_DELIVERY)) {
+      return;
+    }
+
+    assert(before == WAITING || before == TIMED_OUT);
+
+    if (before == TIMED_OUT) {
+      return;
+    }
+
+    assert(before == WAITING);
+    state_.store(LATE_DELIVERY, std::memory_order_release);
+    state_.futexWake(1);
+  }
+
+  /// Waits until post() has been called in the current Baton lifetime.
+  /// May be called at most once during a Baton lifetime (construction
+  /// |reset until destruction|reset).  If post is called before wait in
+  /// the current lifetime then this method returns immediately.
+  ///
+  /// The restriction that there can be at most one wait() per lifetime
+  /// could be relaxed somewhat without any perf or size regressions,
+  /// but by making this condition very restrictive we can provide better
+  /// checking in debug builds.
+  void wait() {
+    if (spinWaitForEarlyDelivery()) {
+      assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY);
+      return;
+    }
+
+    // guess we have to block :(
+    uint32_t expected = INIT;
+    if (!state_.compare_exchange_strong(expected, WAITING)) {
+      // CAS failed, last minute reprieve
+      assert(expected == EARLY_DELIVERY);
+      return;
+    }
+
+    while (true) {
+      detail::MemoryIdler::futexWait(state_, WAITING);
+
+      // state_ is the truth even if FUTEX_WAIT reported a matching
+      // FUTEX_WAKE, since we aren't using type-stable storage and we
+      // don't guarantee reuse.  The scenario goes like this: thread
+      // A's last touch of a Baton is a call to wake(), which stores
+      // LATE_DELIVERY and gets an unlucky context switch before delivering
+      // the corresponding futexWake.  Thread B sees LATE_DELIVERY
+      // without consuming a futex event, because it calls futexWait
+      // with an expected value of WAITING and hence doesn't go to sleep.
+      // B returns, so the Baton's memory is reused and becomes another
+      // Baton (or a reuse of this one).  B calls futexWait on the new
+      // Baton lifetime, then A wakes up and delivers a spurious futexWake
+      // to the same memory location.  B's futexWait will then report a
+      // consumed wake event even though state_ is still WAITING.
+      //
+      // It would be possible to add an extra state_ dance to communicate
+      // that the futexWake has been sent so that we can be sure to consume
+      // it before returning, but that would be a perf and complexity hit.
+      uint32_t s = state_.load(std::memory_order_acquire);
+      assert(s == WAITING || s == LATE_DELIVERY);
+
+      if (s == LATE_DELIVERY) {
+        return;
+      }
+      // retry
+    }
+  }
+
+  /// Similar to wait, but with a timeout. The thread is unblocked if the
+  /// timeout expires.
+  /// Note: Only a single call to timed_wait/wait is allowed during a baton's
+  /// life-cycle (from construction/reset to destruction/reset). In other
+  /// words, after timed_wait the caller can't invoke wait/timed_wait/try_wait
+  /// again on the same baton without resetting it.
+  ///
+  /// @param  deadline      Time until which the thread can block
+  /// @return               true if the baton was posted to before timeout,
+  ///                       false otherwise
+  template <typename Clock, typename Duration = typename Clock::duration>
+  bool timed_wait(const std::chrono::time_point<Clock,Duration>& deadline) {
+    if (spinWaitForEarlyDelivery()) {
+      assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY);
+      return true;
+    }
+
+    // guess we have to block :(
+    uint32_t expected = INIT;
+    if (!state_.compare_exchange_strong(expected, WAITING)) {
+      // CAS failed, last minute reprieve
+      assert(expected == EARLY_DELIVERY);
+      return true;
+    }
+
+    while (true) {
+      auto rv = state_.futexWaitUntil(WAITING, deadline);
+      if (rv == folly::detail::FutexResult::TIMEDOUT) {
+        state_.store(TIMED_OUT, std::memory_order_release);
+        return false;
+      }
+
+      uint32_t s = state_.load(std::memory_order_acquire);
+      assert(s == WAITING || s == LATE_DELIVERY);
+      if (s == LATE_DELIVERY) {
+        return true;
+      }
+    }
+  }
+
+  /// Similar to wait, but doesn't block the thread if it hasn't been posted.
+  ///
+  /// try_wait has the following semantics:
+  /// - It is ok to call try_wait any number times on the same baton until
+  ///   try_wait reports that the baton has been posted.
+  /// - It is ok to call timed_wait or wait on the same baton if try_wait
+  ///   reports that baton hasn't been posted.
+  /// - If try_wait indicates that the baton has been posted, it is invalid to
+  ///   call wait, try_wait or timed_wait on the same baton without resetting
+  ///
+  /// @return       true if baton has been posted, false othewise
+  bool try_wait() {
+    auto s = state_.load(std::memory_order_acquire);
+    assert(s == INIT || s == EARLY_DELIVERY);
+    return s == EARLY_DELIVERY;
+  }
+
+ private:
+  enum State : uint32_t {
+    INIT = 0,
+    EARLY_DELIVERY = 1,
+    WAITING = 2,
+    LATE_DELIVERY = 3,
+    TIMED_OUT = 4
+  };
+
+  enum {
+    // Must be positive.  If multiple threads are actively using a
+    // higher-level data structure that uses batons internally, it is
+    // likely that the post() and wait() calls happen almost at the same
+    // time.  In this state, we lose big 50% of the time if the wait goes
+    // to sleep immediately.  On circa-2013 devbox hardware it costs about
+    // 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
+    // posix_sem_pingpong test in BatonTests).  We can improve our chances
+    // of EARLY_DELIVERY by spinning for a bit, although we have to balance
+    // this against the loss if we end up sleeping any way.  Spins on this
+    // hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
+    // We give ourself 300 spins, which is about 2 usec of waiting.  As a
+    // partial consolation, since we are using the pause instruction we
+    // are giving a speed boost to the colocated hyperthread.
+    PreBlockAttempts = 300,
+  };
+
+  // Spin for "some time" (see discussion on PreBlockAttempts) waiting
+  // for a post.
+  //
+  // @return       true if we received an early delivery during the wait,
+  //               false otherwise. If the function returns true then
+  //               state_ is guaranteed to be EARLY_DELIVERY
+  bool spinWaitForEarlyDelivery() {
+
+    static_assert(PreBlockAttempts > 0,
+        "isn't this assert clearer than an uninitialized variable warning?");
+    for (int i = 0; i < PreBlockAttempts; ++i) {
+      if (try_wait()) {
+        // hooray!
+        return true;
+      }
+      // The pause instruction is the polite way to spin, but it doesn't
+      // actually affect correctness to omit it if we don't have it.
+      // Pausing donates the full capabilities of the current core to
+      // its other hyperthreads for a dozen cycles or so
+      asm_volatile_pause();
+    }
+
+    return false;
+  }
+
+  detail::Futex<Atom> state_;
+};
+
+} // namespace folly
+
+#endif
diff --git a/faux-folly/folly/Benchmark.cpp b/faux-folly/folly/Benchmark.cpp
new file mode 100644
index 0000000..458e689
--- /dev/null
+++ b/faux-folly/folly/Benchmark.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
+
+#include <folly/Benchmark.h>
+#include <folly/Foreach.h>
+#include <folly/String.h>
+
+#include <algorithm>
+#include <boost/regex.hpp>
+#include <cmath>
+#include <iostream>
+#include <limits>
+#include <utility>
+#include <vector>
+#include <cstring>
+
+using namespace std;
+
+DEFINE_bool(benchmark, false, "Run benchmarks.");
+
+DEFINE_string(bm_regex, "",
+              "Only benchmarks whose names match this regex will be run.");
+
+DEFINE_int64(bm_min_usec, 100,
+             "Minimum # of microseconds we'll accept for each benchmark.");
+
+DEFINE_int64(bm_min_iters, 1,
+             "Minimum # of iterations we'll try for each benchmark.");
+
+DEFINE_int32(bm_max_secs, 1,
+             "Maximum # of seconds we'll spend on each benchmark.");
+
+
+namespace folly {
+
+BenchmarkSuspender::NanosecondsSpent BenchmarkSuspender::nsSpent;
+
+typedef function<detail::TimeIterPair(unsigned int)> BenchmarkFun;
+
+
+vector<tuple<const char*, const char*, BenchmarkFun>>& benchmarks() {
+  static vector<tuple<const char*, const char*, BenchmarkFun>> _benchmarks;
+  return _benchmarks;
+}
+
+#define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE fbFollyGlobalBenchmarkBaseline
+#define FB_STRINGIZE_X2(x) FB_STRINGIZE(x)
+
+// Add the global baseline
+BENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE) {
+#ifdef _MSC_VER
+  _ReadWriteBarrier();
+#else
+  asm volatile("");
+#endif
+}
+
+int getGlobalBenchmarkBaselineIndex() {
+  const char *global = FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE);
+  auto it = std::find_if(
+    benchmarks().begin(),
+    benchmarks().end(),
+    [global](const tuple<const char*, const char*, BenchmarkFun> &v) {
+      return std::strcmp(get<1>(v), global) == 0;
+    }
+  );
+  CHECK(it != benchmarks().end());
+  return it - benchmarks().begin();
+}
+
+#undef FB_STRINGIZE_X2
+#undef FB_FOLLY_GLOBAL_BENCHMARK_BASELINE
+
+void detail::addBenchmarkImpl(const char* file, const char* name,
+                              BenchmarkFun fun) {
+  benchmarks().emplace_back(file, name, std::move(fun));
+}
+
+/**
+ * Given a point, gives density at that point as a number 0.0 < x <=
+ * 1.0. The result is 1.0 if all samples are equal to where, and
+ * decreases near 0 if all points are far away from it. The density is
+ * computed with the help of a radial basis function.
+ */
+static double density(const double * begin, const double *const end,
+                      const double where, const double bandwidth) {
+  assert(begin < end);
+  assert(bandwidth > 0.0);
+  double sum = 0.0;
+  FOR_EACH_RANGE (i, begin, end) {
+    auto d = (*i - where) / bandwidth;
+    sum += exp(- d * d);
+  }
+  return sum / (end - begin);
+}
+
+/**
+ * Computes mean and variance for a bunch of data points. Note that
+ * mean is currently not being used.
+ */
+static pair<double, double>
+meanVariance(const double * begin, const double *const end) {
+  assert(begin < end);
+  double sum = 0.0, sum2 = 0.0;
+  FOR_EACH_RANGE (i, begin, end) {
+    sum += *i;
+    sum2 += *i * *i;
+  }
+  auto const n = end - begin;
+  return make_pair(sum / n, sqrt((sum2 - sum * sum / n) / n));
+}
+
+/**
+ * Computes the mode of a sample set through brute force. Assumes
+ * input is sorted.
+ */
+static double mode(const double * begin, const double *const end) {
+  assert(begin < end);
+  // Lower bound and upper bound for result and their respective
+  // densities.
+  auto
+    result = 0.0,
+    bestDensity = 0.0;
+
+  // Get the variance so we pass it down to density()
+  auto const sigma = meanVariance(begin, end).second;
+  if (!sigma) {
+    // No variance means constant signal
+    return *begin;
+  }
+
+  FOR_EACH_RANGE (i, begin, end) {
+    assert(i == begin || *i >= i[-1]);
+    auto candidate = density(begin, end, *i, sigma * sqrt(2.0));
+    if (candidate > bestDensity) {
+      // Found a new best
+      bestDensity = candidate;
+      result = *i;
+    } else {
+      // Density is decreasing... we could break here if we definitely
+      // knew this is unimodal.
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Given a bunch of benchmark samples, estimate the actual run time.
+ */
+static double estimateTime(double * begin, double * end) {
+  assert(begin < end);
+
+  // Current state of the art: get the minimum. After some
+  // experimentation, it seems taking the minimum is the best.
+
+  return *min_element(begin, end);
+
+  // What follows after estimates the time as the mode of the
+  // distribution.
+
+  // Select the awesomest (i.e. most frequent) result. We do this by
+  // sorting and then computing the longest run length.
+  sort(begin, end);
+
+  // Eliminate outliers. A time much larger than the minimum time is
+  // considered an outlier.
+  while (end[-1] > 2.0 * *begin) {
+    --end;
+    if (begin == end) {
+      LOG(INFO) << *begin;
+    }
+    assert(begin < end);
+  }
+
+  double result = 0;
+
+  /* Code used just for comparison purposes */ {
+    unsigned bestFrequency = 0;
+    unsigned candidateFrequency = 1;
+    double candidateValue = *begin;
+    for (auto current = begin + 1; ; ++current) {
+      if (current == end || *current != candidateValue) {
+        // Done with the current run, see if it was best
+        if (candidateFrequency > bestFrequency) {
+          bestFrequency = candidateFrequency;
+          result = candidateValue;
+        }
+        if (current == end) {
+          break;
+        }
+        // Start a new run
+        candidateValue = *current;
+        candidateFrequency = 1;
+      } else {
+        // Cool, inside a run, increase the frequency
+        ++candidateFrequency;
+      }
+    }
+  }
+
+  result = mode(begin, end);
+
+  return result;
+}
+
+static double runBenchmarkGetNSPerIteration(const BenchmarkFun& fun,
+                                            const double globalBaseline) {
+  // They key here is accuracy; too low numbers means the accuracy was
+  // coarse. We up the ante until we get to at least minNanoseconds
+  // timings.
+  static uint64_t resolutionInNs = 0, coarseResolutionInNs = 0;
+  if (!resolutionInNs) {
+    timespec ts;
+    CHECK_EQ(0, clock_getres(detail::DEFAULT_CLOCK_ID, &ts));
+    CHECK_EQ(0, ts.tv_sec) << "Clock sucks.";
+    CHECK_LT(0, ts.tv_nsec) << "Clock too fast for its own good.";
+    CHECK_EQ(1, ts.tv_nsec) << "Clock too coarse, upgrade your kernel.";
+    resolutionInNs = ts.tv_nsec;
+  }
+  // We choose a minimum minimum (sic) of 100,000 nanoseconds, but if
+  // the clock resolution is worse than that, it will be larger. In
+  // essence we're aiming at making the quantization noise 0.01%.
+  static const auto minNanoseconds =
+    max<uint64_t>(FLAGS_bm_min_usec * 1000UL,
+        min<uint64_t>(resolutionInNs * 100000, 1000000000ULL));
+
+  // We do measurements in several epochs and take the minimum, to
+  // account for jitter.
+  static const unsigned int epochs = 1000;
+  // We establish a total time budget as we don't want a measurement
+  // to take too long. This will curtail the number of actual epochs.
+  const uint64_t timeBudgetInNs = FLAGS_bm_max_secs * 1000000000ULL;
+  timespec global;
+  CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &global));
+
+  double epochResults[epochs] = { 0 };
+  size_t actualEpochs = 0;
+
+  for (; actualEpochs < epochs; ++actualEpochs) {
+    for (unsigned int n = FLAGS_bm_min_iters; n < (1UL << 30); n *= 2) {
+      auto const nsecsAndIter = fun(n);
+      if (nsecsAndIter.first < minNanoseconds) {
+        continue;
+      }
+      // We got an accurate enough timing, done. But only save if
+      // smaller than the current result.
+      epochResults[actualEpochs] = max(0.0, double(nsecsAndIter.first) /
+                                       nsecsAndIter.second - globalBaseline);
+      // Done with the current epoch, we got a meaningful timing.
+      break;
+    }
+    timespec now;
+    CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &now));
+    if (detail::timespecDiff(now, global) >= timeBudgetInNs) {
+      // No more time budget available.
+      ++actualEpochs;
+      break;
+    }
+  }
+
+  // If the benchmark was basically drowned in baseline noise, it's
+  // possible it became negative.
+  return max(0.0, estimateTime(epochResults, epochResults + actualEpochs));
+}
+
+struct ScaleInfo {
+  double boundary;
+  const char* suffix;
+};
+
+static const ScaleInfo kTimeSuffixes[] {
+  { 365.25 * 24 * 3600, "years" },
+  { 24 * 3600, "days" },
+  { 3600, "hr" },
+  { 60, "min" },
+  { 1, "s" },
+  { 1E-3, "ms" },
+  { 1E-6, "us" },
+  { 1E-9, "ns" },
+  { 1E-12, "ps" },
+  { 1E-15, "fs" },
+  { 0, nullptr },
+};
+
+static const ScaleInfo kMetricSuffixes[] {
+  { 1E24, "Y" },  // yotta
+  { 1E21, "Z" },  // zetta
+  { 1E18, "X" },  // "exa" written with suffix 'X' so as to not create
+                  //   confusion with scientific notation
+  { 1E15, "P" },  // peta
+  { 1E12, "T" },  // terra
+  { 1E9, "G" },   // giga
+  { 1E6, "M" },   // mega
+  { 1E3, "K" },   // kilo
+  { 1, "" },
+  { 1E-3, "m" },  // milli
+  { 1E-6, "u" },  // micro
+  { 1E-9, "n" },  // nano
+  { 1E-12, "p" }, // pico
+  { 1E-15, "f" }, // femto
+  { 1E-18, "a" }, // atto
+  { 1E-21, "z" }, // zepto
+  { 1E-24, "y" }, // yocto
+  { 0, nullptr },
+};
+
+static string humanReadable(double n, unsigned int decimals,
+                            const ScaleInfo* scales) {
+  if (std::isinf(n) || std::isnan(n)) {
+    return folly::to<string>(n);
+  }
+
+  const double absValue = fabs(n);
+  const ScaleInfo* scale = scales;
+  while (absValue < scale[0].boundary && scale[1].suffix != nullptr) {
+    ++scale;
+  }
+
+  const double scaledValue = n / scale->boundary;
+  return stringPrintf("%.*f%s", decimals, scaledValue, scale->suffix);
+}
+
+static string readableTime(double n, unsigned int decimals) {
+  return humanReadable(n, decimals, kTimeSuffixes);
+}
+
+static string metricReadable(double n, unsigned int decimals) {
+  return humanReadable(n, decimals, kMetricSuffixes);
+}
+
+static void printBenchmarkResultsAsTable(
+  const vector<tuple<const char*, const char*, double> >& data) {
+  // Width available
+  static const unsigned int columns = 76;
+
+  // Compute the longest benchmark name
+  size_t longestName = 0;
+  FOR_EACH_RANGE (i, 1, benchmarks().size()) {
+    longestName = max(longestName, strlen(get<1>(benchmarks()[i])));
+  }
+
+  // Print a horizontal rule
+  auto separator = [&](char pad) {
+    puts(string(columns, pad).c_str());
+  };
+
+  // Print header for a file
+  auto header = [&](const char* file) {
+    separator('=');
+    printf("%-*srelative  time/iter  iters/s\n",
+           columns - 28, file);
+    separator('=');
+  };
+
+  double baselineNsPerIter = numeric_limits<double>::max();
+  const char* lastFile = "";
+
+  for (auto& datum : data) {
+    auto file = get<0>(datum);
+    if (strcmp(file, lastFile)) {
+      // New file starting
+      header(file);
+      lastFile = file;
+    }
+
+    string s = get<1>(datum);
+    if (s == "-") {
+      separator('-');
+      continue;
+    }
+    bool useBaseline /* = void */;
+    if (s[0] == '%') {
+      s.erase(0, 1);
+      useBaseline = true;
+    } else {
+      baselineNsPerIter = get<2>(datum);
+      useBaseline = false;
+    }
+    s.resize(columns - 29, ' ');
+    auto nsPerIter = get<2>(datum);
+    auto secPerIter = nsPerIter / 1E9;
+    auto itersPerSec = 1 / secPerIter;
+    if (!useBaseline) {
+      // Print without baseline
+      printf("%*s           %9s  %7s\n",
+             static_cast<int>(s.size()), s.c_str(),
+             readableTime(secPerIter, 2).c_str(),
+             metricReadable(itersPerSec, 2).c_str());
+    } else {
+      // Print with baseline
+      auto rel = baselineNsPerIter / nsPerIter * 100.0;
+      printf("%*s %7.2f%%  %9s  %7s\n",
+             static_cast<int>(s.size()), s.c_str(),
+             rel,
+             readableTime(secPerIter, 2).c_str(),
+             metricReadable(itersPerSec, 2).c_str());
+    }
+  }
+  separator('=');
+}
+
+static void printBenchmarkResults(
+  const vector<tuple<const char*, const char*, double> >& data) {
+
+  printBenchmarkResultsAsTable(data);
+}
+
+void runBenchmarks() {
+  CHECK(!benchmarks().empty());
+
+  vector<tuple<const char*, const char*, double>> results;
+  results.reserve(benchmarks().size() - 1);
+
+  std::unique_ptr<boost::regex> bmRegex;
+  if (!FLAGS_bm_regex.empty()) {
+    bmRegex.reset(new boost::regex(FLAGS_bm_regex));
+  }
+
+  // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS.
+
+  unsigned int baselineIndex = getGlobalBenchmarkBaselineIndex();
+
+  auto const globalBaseline =
+      runBenchmarkGetNSPerIteration(get<2>(benchmarks()[baselineIndex]), 0);
+  FOR_EACH_RANGE (i, 0, benchmarks().size()) {
+    if (i == baselineIndex) {
+      continue;
+    }
+    double elapsed = 0.0;
+    if (strcmp(get<1>(benchmarks()[i]), "-") != 0) { // skip separators
+      if (bmRegex && !boost::regex_search(get<1>(benchmarks()[i]), *bmRegex)) {
+        continue;
+      }
+      elapsed = runBenchmarkGetNSPerIteration(get<2>(benchmarks()[i]),
+                                              globalBaseline);
+    }
+    results.emplace_back(get<0>(benchmarks()[i]),
+                         get<1>(benchmarks()[i]), elapsed);
+  }
+
+  // PLEASE MAKE NOISE. MEASUREMENTS DONE.
+
+  printBenchmarkResults(results);
+}
+
+} // namespace folly
diff --git a/faux-folly/folly/Benchmark.h b/faux-folly/folly/Benchmark.h
new file mode 100644
index 0000000..c25ca28
--- /dev/null
+++ b/faux-folly/folly/Benchmark.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BENCHMARK_H_
+#define FOLLY_BENCHMARK_H_
+
+#include <folly/ScopeGuard.h>
+#include <folly/Portability.h>
+#include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE
+#include <cassert>
+#include <ctime>
+#include <boost/function_types/function_arity.hpp>
+#include <functional>
+#include <glog/logging.h>
+#include <gflags/gflags.h>
+#include <limits>
+#include <type_traits>
+
+DECLARE_bool(benchmark);
+
+namespace folly {
+
+/**
+ * Runs all benchmarks defined. Usually put in main().
+ */
+void runBenchmarks();
+
+/**
+ * Runs all benchmarks defined if and only if the --benchmark flag has
+ * been passed to the program. Usually put in main().
+ */
+inline bool runBenchmarksOnFlag() {
+  if (FLAGS_benchmark) {
+    runBenchmarks();
+  }
+  return FLAGS_benchmark;
+}
+
+namespace detail {
+
+/**
+ * This is the clock ID used for measuring time. On older kernels, the
+ * resolution of this clock will be very coarse, which will cause the
+ * benchmarks to fail.
+ */
+enum Clock { DEFAULT_CLOCK_ID = CLOCK_REALTIME };
+
+typedef std::pair<uint64_t, unsigned int> TimeIterPair;
+
+/**
+ * Adds a benchmark wrapped in a std::function. Only used
+ * internally. Pass by value is intentional.
+ */
+void addBenchmarkImpl(const char* file,
+                      const char* name,
+                      std::function<TimeIterPair(unsigned int)>);
+
+/**
+ * Takes the difference between two timespec values. end is assumed to
+ * occur after start.
+ */
+inline uint64_t timespecDiff(timespec end, timespec start) {
+  if (end.tv_sec == start.tv_sec) {
+    assert(end.tv_nsec >= start.tv_nsec);
+    return end.tv_nsec - start.tv_nsec;
+  }
+  assert(end.tv_sec > start.tv_sec);
+  auto diff = uint64_t(end.tv_sec - start.tv_sec);
+  assert(diff <
+         std::numeric_limits<uint64_t>::max() / 1000000000UL);
+  return diff * 1000000000UL
+    + end.tv_nsec - start.tv_nsec;
+}
+
+/**
+ * Takes the difference between two sets of timespec values. The first
+ * two come from a high-resolution clock whereas the other two come
+ * from a low-resolution clock. The crux of the matter is that
+ * high-res values may be bogus as documented in
+ * http://linux.die.net/man/3/clock_gettime. The trouble is when the
+ * running process migrates from one CPU to another, which is more
+ * likely for long-running processes. Therefore we watch for high
+ * differences between the two timings.
+ *
+ * This function is subject to further improvements.
+ */
+inline uint64_t timespecDiff(timespec end, timespec start,
+                             timespec endCoarse, timespec startCoarse) {
+  auto fine = timespecDiff(end, start);
+  auto coarse = timespecDiff(endCoarse, startCoarse);
+  if (coarse - fine >= 1000000) {
+    // The fine time is in all likelihood bogus
+    return coarse;
+  }
+  return fine;
+}
+
+} // namespace detail
+
+/**
+ * Supporting type for BENCHMARK_SUSPEND defined below.
+ */
+struct BenchmarkSuspender {
+  BenchmarkSuspender() {
+    CHECK_EQ(0, clock_gettime(detail::DEFAULT_CLOCK_ID, &start));
+  }
+
+  BenchmarkSuspender(const BenchmarkSuspender &) = delete;
+  BenchmarkSuspender(BenchmarkSuspender && rhs) noexcept {
+    start = rhs.start;
+    rhs.start.tv_nsec = rhs.start.tv_sec = 0;
+  }
+
+  BenchmarkSuspender& operator=(const BenchmarkSuspender &) = delete;
+  BenchmarkSuspender& operator=(BenchmarkSuspender && rhs) {
+    if (start.tv_nsec > 0 || start.tv_sec > 0) {
+      tally();
+    }
+    start = rhs.start;
+    rhs.start.tv_nsec = rhs.start.tv_sec = 0;
+    return *this;
+  }
+
+  ~BenchmarkSuspender() {
+    if (start.tv_nsec > 0 || start.tv_sec > 0) {
+      tally();
+    }
+  }
+
+  void dismiss() {
+    assert(start.tv_nsec > 0 || start.tv_sec > 0);
+    tally();
+    start.tv_nsec = start.tv_sec = 0;
+  }
+
+  void rehire() {
+    assert(start.tv_nsec == 0 || start.tv_sec == 0);
+    CHECK_EQ(0, clock_gettime(detail::DEFAULT_CLOCK_ID, &start));
+  }
+
+  template <class F>
+  auto dismissing(F f) -> typename std::result_of<F()>::type {
+    SCOPE_EXIT { rehire(); };
+    dismiss();
+    return f();
+  }
+
+  /**
+   * This is for use inside of if-conditions, used in BENCHMARK macros.
+   * If-conditions bypass the explicit on operator bool.
+   */
+  explicit operator bool() const {
+    return false;
+  }
+
+  /**
+   * Accumulates nanoseconds spent outside benchmark.
+   */
+  typedef uint64_t NanosecondsSpent;
+  static NanosecondsSpent nsSpent;
+
+private:
+  void tally() {
+    timespec end;
+    CHECK_EQ(0, clock_gettime(detail::DEFAULT_CLOCK_ID, &end));
+    nsSpent += detail::timespecDiff(end, start);
+    start = end;
+  }
+
+  timespec start;
+};
+
+/**
+ * Adds a benchmark. Usually not called directly but instead through
+ * the macro BENCHMARK defined below. The lambda function involved
+ * must take exactly one parameter of type unsigned, and the benchmark
+ * uses it with counter semantics (iteration occurs inside the
+ * function).
+ */
+template <typename Lambda>
+typename std::enable_if<
+  boost::function_types::function_arity<decltype(&Lambda::operator())>::value
+  == 2
+>::type
+addBenchmark(const char* file, const char* name, Lambda&& lambda) {
+  auto execute = [=](unsigned int times) {
+    BenchmarkSuspender::nsSpent = 0;
+    timespec start, end;
+    unsigned int niter;
+
+    // CORE MEASUREMENT STARTS
+    auto const r1 = clock_gettime(detail::DEFAULT_CLOCK_ID, &start);
+    niter = lambda(times);
+    auto const r2 = clock_gettime(detail::DEFAULT_CLOCK_ID, &end);
+    // CORE MEASUREMENT ENDS
+
+    CHECK_EQ(0, r1);
+    CHECK_EQ(0, r2);
+
+    return detail::TimeIterPair(
+      detail::timespecDiff(end, start) - BenchmarkSuspender::nsSpent,
+      niter);
+  };
+
+  detail::addBenchmarkImpl(file, name,
+    std::function<detail::TimeIterPair(unsigned int)>(execute));
+}
+
+/**
+ * Adds a benchmark. Usually not called directly but instead through
+ * the macro BENCHMARK defined below. The lambda function involved
+ * must take zero parameters, and the benchmark calls it repeatedly
+ * (iteration occurs outside the function).
+ */
+template <typename Lambda>
+typename std::enable_if<
+  boost::function_types::function_arity<decltype(&Lambda::operator())>::value
+  == 1
+>::type
+addBenchmark(const char* file, const char* name, Lambda&& lambda) {
+  addBenchmark(file, name, [=](unsigned int times) {
+      unsigned int niter = 0;
+      while (times-- > 0) {
+        niter += lambda();
+      }
+      return niter;
+    });
+}
+
+/**
+ * Call doNotOptimizeAway(var) against variables that you use for
+ * benchmarking but otherwise are useless. The compiler tends to do a
+ * good job at eliminating unused variables, and this function fools
+ * it into thinking var is in fact needed.
+ */
+#ifdef _MSC_VER
+
+#pragma optimize("", off)
+
+template <class T>
+void doNotOptimizeAway(T&& datum) {
+  datum = datum;
+}
+
+#pragma optimize("", on)
+
+#elif defined(__clang__)
+
+template <class T>
+__attribute__((__optnone__)) void doNotOptimizeAway(T&& datum) {
+}
+
+#else
+
+template <class T>
+void doNotOptimizeAway(T&& datum) {
+  asm volatile("" : "+r" (datum));
+}
+
+#endif
+
+} // namespace folly
+
+/**
+ * Introduces a benchmark function. Used internally, see BENCHMARK and
+ * friends below.
+ */
+#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName)   \
+  static void funName(paramType);                                       \
+  static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = (           \
+    ::folly::addBenchmark(__FILE__, stringName,                         \
+      [](paramType paramName) -> unsigned { funName(paramName);         \
+                                            return rv; }),              \
+    true);                                                              \
+  static void funName(paramType paramName)
+
+/**
+ * Introduces a benchmark function with support for returning the actual
+ * number of iterations. Used internally, see BENCHMARK_MULTI and friends
+ * below.
+ */
+#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \
+  static unsigned funName(paramType);                                   \
+  static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = (           \
+    ::folly::addBenchmark(__FILE__, stringName,                         \
+      [](paramType paramName) { return funName(paramName); }),          \
+    true);                                                              \
+  static unsigned funName(paramType paramName)
+
+/**
+ * Introduces a benchmark function. Use with either one or two arguments.
+ * The first is the name of the benchmark. Use something descriptive, such
+ * as insertVectorBegin. The second argument may be missing, or could be a
+ * symbolic counter. The counter dictates how many internal iteration the
+ * benchmark does. Example:
+ *
+ * BENCHMARK(vectorPushBack) {
+ *   vector<int> v;
+ *   v.push_back(42);
+ * }
+ *
+ * BENCHMARK(insertVectorBegin, n) {
+ *   vector<int> v;
+ *   FOR_EACH_RANGE (i, 0, n) {
+ *     v.insert(v.begin(), 42);
+ *   }
+ * }
+ */
+#define BENCHMARK(name, ...)                                    \
+  BENCHMARK_IMPL(                                               \
+    name,                                                       \
+    FB_STRINGIZE(name),                                         \
+    FB_ARG_2_OR_1(1, ## __VA_ARGS__),                           \
+    FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
+    __VA_ARGS__)
+
+/**
+ * Like BENCHMARK above, but allows the user to return the actual
+ * number of iterations executed in the function body. This can be
+ * useful if the benchmark function doesn't know upfront how many
+ * iterations it's going to run or if it runs through a certain
+ * number of test cases, e.g.:
+ *
+ * BENCHMARK_MULTI(benchmarkSomething) {
+ *   std::vector<int> testCases { 0, 1, 1, 2, 3, 5 };
+ *   for (int c : testCases) {
+ *     doSomething(c);
+ *   }
+ *   return testCases.size();
+ * }
+ */
+#define BENCHMARK_MULTI(name, ...)                              \
+  BENCHMARK_MULTI_IMPL(                                         \
+    name,                                                       \
+    FB_STRINGIZE(name),                                         \
+    FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
+    __VA_ARGS__)
+
+/**
+ * Defines a benchmark that passes a parameter to another one. This is
+ * common for benchmarks that need a "problem size" in addition to
+ * "number of iterations". Consider:
+ *
+ * void pushBack(uint n, size_t initialSize) {
+ *   vector<int> v;
+ *   BENCHMARK_SUSPEND {
+ *     v.resize(initialSize);
+ *   }
+ *   FOR_EACH_RANGE (i, 0, n) {
+ *    v.push_back(i);
+ *   }
+ * }
+ * BENCHMARK_PARAM(pushBack, 0)
+ * BENCHMARK_PARAM(pushBack, 1000)
+ * BENCHMARK_PARAM(pushBack, 1000000)
+ *
+ * The benchmark above estimates the speed of push_back at different
+ * initial sizes of the vector. The framework will pass 0, 1000, and
+ * 1000000 for initialSize, and the iteration count for n.
+ */
+#define BENCHMARK_PARAM(name, param)                                    \
+  BENCHMARK_NAMED_PARAM(name, param, param)
+
+/**
+ * Same as BENCHMARK_PARAM, but allows to return the actual number of
+ * iterations that have been run.
+ */
+#define BENCHMARK_PARAM_MULTI(name, param)                              \
+  BENCHMARK_NAMED_PARAM_MULTI(name, param, param)
+
+/*
+ * Like BENCHMARK_PARAM(), but allows a custom name to be specified for each
+ * parameter, rather than using the parameter value.
+ *
+ * Useful when the parameter value is not a valid token for string pasting,
+ * of when you want to specify multiple parameter arguments.
+ *
+ * For example:
+ *
+ * void addValue(uint n, int64_t bucketSize, int64_t min, int64_t max) {
+ *   Histogram<int64_t> hist(bucketSize, min, max);
+ *   int64_t num = min;
+ *   FOR_EACH_RANGE (i, 0, n) {
+ *     hist.addValue(num);
+ *     ++num;
+ *     if (num > max) { num = min; }
+ *   }
+ * }
+ *
+ * BENCHMARK_NAMED_PARAM(addValue, 0_to_100, 1, 0, 100)
+ * BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000)
+ * BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000)
+ */
+#define BENCHMARK_NAMED_PARAM(name, param_name, ...)                    \
+  BENCHMARK_IMPL(                                                       \
+      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
+      FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",              \
+      iters,                                                            \
+      unsigned,                                                         \
+      iters) {                                                          \
+    name(iters, ## __VA_ARGS__);                                        \
+  }
+
+/**
+ * Same as BENCHMARK_NAMED_PARAM, but allows to return the actual number
+ * of iterations that have been run.
+ */
+#define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...)              \
+  BENCHMARK_MULTI_IMPL(                                                 \
+      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
+      FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",              \
+      unsigned,                                                         \
+      iters) {                                                          \
+    return name(iters, ## __VA_ARGS__);                                 \
+  }
+
+/**
+ * Just like BENCHMARK, but prints the time relative to a
+ * baseline. The baseline is the most recent BENCHMARK() seen in
+ * lexical order. Example:
+ *
+ * // This is the baseline
+ * BENCHMARK(insertVectorBegin, n) {
+ *   vector<int> v;
+ *   FOR_EACH_RANGE (i, 0, n) {
+ *     v.insert(v.begin(), 42);
+ *   }
+ * }
+ *
+ * BENCHMARK_RELATIVE(insertListBegin, n) {
+ *   list<int> s;
+ *   FOR_EACH_RANGE (i, 0, n) {
+ *     s.insert(s.begin(), 42);
+ *   }
+ * }
+ *
+ * Any number of relative benchmark can be associated with a
+ * baseline. Another BENCHMARK() occurrence effectively establishes a
+ * new baseline.
+ */
+#define BENCHMARK_RELATIVE(name, ...)                           \
+  BENCHMARK_IMPL(                                               \
+    name,                                                       \
+    "%" FB_STRINGIZE(name),                                     \
+    FB_ARG_2_OR_1(1, ## __VA_ARGS__),                           \
+    FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
+    __VA_ARGS__)
+
+/**
+ * Same as BENCHMARK_RELATIVE, but allows to return the actual number
+ * of iterations that have been run.
+ */
+#define BENCHMARK_RELATIVE_MULTI(name, ...)                     \
+  BENCHMARK_MULTI_IMPL(                                         \
+    name,                                                       \
+    "%" FB_STRINGIZE(name),                                     \
+    FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
+    __VA_ARGS__)
+
+/**
+ * A combination of BENCHMARK_RELATIVE and BENCHMARK_PARAM.
+ */
+#define BENCHMARK_RELATIVE_PARAM(name, param)                           \
+  BENCHMARK_RELATIVE_NAMED_PARAM(name, param, param)
+
+/**
+ * Same as BENCHMARK_RELATIVE_PARAM, but allows to return the actual
+ * number of iterations that have been run.
+ */
+#define BENCHMARK_RELATIVE_PARAM_MULTI(name, param)                     \
+  BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param, param)
+
+/**
+ * A combination of BENCHMARK_RELATIVE and BENCHMARK_NAMED_PARAM.
+ */
+#define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...)           \
+  BENCHMARK_IMPL(                                                       \
+      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
+      "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",          \
+      iters,                                                            \
+      unsigned,                                                         \
+      iters) {                                                          \
+    name(iters, ## __VA_ARGS__);                                        \
+  }
+
+/**
+ * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows to return the
+ * actual number of iterations that have been run.
+ */
+#define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...)     \
+  BENCHMARK_MULTI_IMPL(                                                 \
+      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
+      "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",          \
+      unsigned,                                                         \
+      iters) {                                                          \
+    return name(iters, ## __VA_ARGS__);                                 \
+  }
+
+/**
+ * Draws a line of dashes.
+ */
+#define BENCHMARK_DRAW_LINE()                                             \
+  static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = (             \
+    ::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \
+    true);
+
+/**
+ * Allows execution of code that doesn't count torward the benchmark's
+ * time budget. Example:
+ *
+ * BENCHMARK_START_GROUP(insertVectorBegin, n) {
+ *   vector<int> v;
+ *   BENCHMARK_SUSPEND {
+ *     v.reserve(n);
+ *   }
+ *   FOR_EACH_RANGE (i, 0, n) {
+ *     v.insert(v.begin(), 42);
+ *   }
+ * }
+ */
+#define BENCHMARK_SUSPEND                               \
+  if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) =   \
+      ::folly::BenchmarkSuspender()) {}                 \
+  else
+
+#endif // FOLLY_BENCHMARK_H_
diff --git a/faux-folly/folly/Bits.h b/faux-folly/folly/Bits.h
new file mode 100644
index 0000000..3fddca9
--- /dev/null
+++ b/faux-folly/folly/Bits.h
@@ -0,0 +1,621 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Various low-level, bit-manipulation routines.
+ *
+ * findFirstSet(x)  [constexpr]
+ *    find first (least significant) bit set in a value of an integral type,
+ *    1-based (like ffs()).  0 = no bits are set (x == 0)
+ *
+ * findLastSet(x)  [constexpr]
+ *    find last (most significant) bit set in a value of an integral type,
+ *    1-based.  0 = no bits are set (x == 0)
+ *    for x != 0, findLastSet(x) == 1 + floor(log2(x))
+ *
+ * nextPowTwo(x)  [constexpr]
+ *    Finds the next power of two >= x.
+ *
+ * isPowTwo(x)  [constexpr]
+ *    return true iff x is a power of two
+ *
+ * popcount(x)
+ *    return the number of 1 bits in x
+ *
+ * Endian
+ *    convert between native, big, and little endian representation
+ *    Endian::big(x)      big <-> native
+ *    Endian::little(x)   little <-> native
+ *    Endian::swap(x)     big <-> little
+ *
+ * BitIterator
+ *    Wrapper around an iterator over an integral type that iterates
+ *    over its underlying bits in MSb to LSb order
+ *
+ * findFirstSet(BitIterator begin, BitIterator end)
+ *    return a BitIterator pointing to the first 1 bit in [begin, end), or
+ *    end if all bits in [begin, end) are 0
+ *
+ * @author Tudor Bosman (tudorb@fb.com)
+ */
+
+#ifndef FOLLY_BITS_H_
+#define FOLLY_BITS_H_
+
+#if !defined(__clang__) && !defined(_MSC_VER)
+#define FOLLY_INTRINSIC_CONSTEXPR constexpr
+#else
+// GCC is the only compiler with intrinsics constexpr.
+#define FOLLY_INTRINSIC_CONSTEXPR const
+#endif
+
+#include <folly/Portability.h>
+
+#include <folly/detail/BitsDetail.h>
+#include <folly/detail/BitIteratorDetail.h>
+#include <folly/Likely.h>
+
+#if FOLLY_HAVE_BYTESWAP_H
+# include <byteswap.h>
+#endif
+
+#ifdef _MSC_VER
+# include <intrin.h>
+# pragma intrinsic(_BitScanForward)
+# pragma intrinsic(_BitScanForward64)
+# pragma intrinsic(_BitScanReverse)
+# pragma intrinsic(_BitScanReverse64)
+#endif
+
+#include <cassert>
+#include <cinttypes>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <stdint.h>
+
+namespace folly {
+
+// Generate overloads for findFirstSet as wrappers around
+// appropriate ffs, ffsl, ffsll gcc builtins
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) <= sizeof(unsigned int)),
+  unsigned int>::type
+  findFirstSet(T x) {
+#ifdef _MSC_VER
+  unsigned long index;
+  return _BitScanForward(&index, x) ? index : 0;
+#else
+  return __builtin_ffs(x);
+#endif
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) > sizeof(unsigned int) &&
+   sizeof(T) <= sizeof(unsigned long)),
+  unsigned int>::type
+  findFirstSet(T x) {
+#ifdef _MSC_VER
+  unsigned long index;
+  return _BitScanForward(&index, x) ? index : 0;
+#else
+  return __builtin_ffsl(x);
+#endif
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) > sizeof(unsigned long) &&
+   sizeof(T) <= sizeof(unsigned long long)),
+  unsigned int>::type
+  findFirstSet(T x) {
+#ifdef _MSC_VER
+  unsigned long index;
+  return _BitScanForward64(&index, x) ? index : 0;
+#else
+  return __builtin_ffsll(x);
+#endif
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value && std::is_signed<T>::value),
+  unsigned int>::type
+  findFirstSet(T x) {
+  // Note that conversion from a signed type to the corresponding unsigned
+  // type is technically implementation-defined, but will likely work
+  // on any impementation that uses two's complement.
+  return findFirstSet(static_cast<typename std::make_unsigned<T>::type>(x));
+}
+
+// findLastSet: return the 1-based index of the highest bit set
+// for x > 0, findLastSet(x) == 1 + floor(log2(x))
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) <= sizeof(unsigned int)),
+  unsigned int>::type
+  findLastSet(T x) {
+#ifdef _MSC_VER
+  unsigned long index;
+  int clz;
+  if (_BitScanReverse(&index, x)) {
+    clz = static_cast<int>(31 - index);
+  } else {
+    clz = 32;
+  }
+  return x ? 8 * sizeof(unsigned int) - clz : 0;
+#else
+  return x ? 8 * sizeof(unsigned int) - __builtin_clz(x) : 0;
+#endif
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) > sizeof(unsigned int) &&
+   sizeof(T) <= sizeof(unsigned long)),
+  unsigned int>::type
+  findLastSet(T x) {
+#ifdef _MSC_VER
+  unsigned long index;
+  int clz;
+  if (_BitScanReverse(&index, x)) {
+    clz = static_cast<int>(31 - index);
+  } else {
+    clz = 32;
+  }
+  return x ? 8 * sizeof(unsigned int) - clz : 0;
+#else
+  return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
+#endif
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) > sizeof(unsigned long) &&
+   sizeof(T) <= sizeof(unsigned long long)),
+  unsigned int>::type
+  findLastSet(T x) {
+#ifdef _MSC_VER
+  unsigned long index;
+  unsigned long long clz;
+  if (_BitScanReverse(&index, x)) {
+    clz = static_cast<unsigned long long>(63 - index);
+  } else {
+    clz = 64;
+  }
+  return x ? 8 * sizeof(unsigned long long) - clz : 0;
+#else
+  return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
+#endif
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_signed<T>::value),
+  unsigned int>::type
+  findLastSet(T x) {
+  return findLastSet(static_cast<typename std::make_unsigned<T>::type>(x));
+}
+
+template <class T>
+inline FOLLY_INTRINSIC_CONSTEXPR
+typename std::enable_if<
+  std::is_integral<T>::value && std::is_unsigned<T>::value,
+  T>::type
+nextPowTwo(T v) {
+  return v ? (1ul << findLastSet(v - 1)) : 1;
+}
+
+template <class T>
+inline constexpr
+typename std::enable_if<
+  std::is_integral<T>::value && std::is_unsigned<T>::value,
+  bool>::type
+isPowTwo(T v) {
+  return (v != 0) && !(v & (v - 1));
+}
+
+/**
+ * Population count
+ */
+template <class T>
+inline typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) <= sizeof(unsigned int)),
+  size_t>::type
+  popcount(T x) {
+  return __builtin_popcount(x);
+}
+
+template <class T>
+inline typename std::enable_if<
+  (std::is_integral<T>::value &&
+   std::is_unsigned<T>::value &&
+   sizeof(T) > sizeof(unsigned int) &&
+   sizeof(T) <= sizeof(unsigned long long)),
+  size_t>::type
+  popcount(T x) {
+  return __builtin_popcountll(x);
+}
+
+/**
+ * Endianness detection and manipulation primitives.
+ */
+namespace detail {
+
+template <class T>
+struct EndianIntBase {
+ public:
+  static T swap(T x);
+};
+
+#ifndef _MSC_VER
+
+/**
+ * If we have the bswap_16 macro from byteswap.h, use it; otherwise, provide our
+ * own definition.
+ */
+#ifdef bswap_16
+# define our_bswap16 bswap_16
+#else
+
+template<class Int16>
+inline constexpr typename std::enable_if<
+  sizeof(Int16) == 2,
+  Int16>::type
+our_bswap16(Int16 x) {
+  return ((x >> 8) & 0xff) | ((x & 0xff) << 8);
+}
+#endif
+
+#endif
+
+#define FB_GEN(t, fn) \
+template<> inline t EndianIntBase<t>::swap(t x) { return fn(x); }
+
+// fn(x) expands to (x) if the second argument is empty, which is exactly
+// what we want for [u]int8_t. Also, gcc 4.7 on Intel doesn't have
+// __builtin_bswap16 for some reason, so we have to provide our own.
+FB_GEN( int8_t,)
+FB_GEN(uint8_t,)
+#ifdef _MSC_VER
+FB_GEN( int64_t, _byteswap_uint64)
+FB_GEN(uint64_t, _byteswap_uint64)
+FB_GEN( int32_t, _byteswap_ulong)
+FB_GEN(uint32_t, _byteswap_ulong)
+FB_GEN( int16_t, _byteswap_ushort)
+FB_GEN(uint16_t, _byteswap_ushort)
+#else
+FB_GEN( int64_t, __builtin_bswap64)
+FB_GEN(uint64_t, __builtin_bswap64)
+FB_GEN( int32_t, __builtin_bswap32)
+FB_GEN(uint32_t, __builtin_bswap32)
+FB_GEN( int16_t, our_bswap16)
+FB_GEN(uint16_t, our_bswap16)
+#endif
+
+#undef FB_GEN
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+template <class T>
+struct EndianInt : public detail::EndianIntBase<T> {
+ public:
+  static T big(T x) { return EndianInt::swap(x); }
+  static T little(T x) { return x; }
+};
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+template <class T>
+struct EndianInt : public detail::EndianIntBase<T> {
+ public:
+  static T big(T x) { return x; }
+  static T little(T x) { return EndianInt::swap(x); }
+};
+
+#else
+# error Your machine uses a weird endianness!
+#endif  /* __BYTE_ORDER__ */
+
+}  // namespace detail
+
+// big* convert between native and big-endian representations
+// little* convert between native and little-endian representations
+// swap* convert between big-endian and little-endian representations
+//
+// ntohs, htons == big16
+// ntohl, htonl == big32
+#define FB_GEN1(fn, t, sz) \
+  static t fn##sz(t x) { return fn<t>(x); } \
+
+#define FB_GEN2(t, sz) \
+  FB_GEN1(swap, t, sz) \
+  FB_GEN1(big, t, sz) \
+  FB_GEN1(little, t, sz)
+
+#define FB_GEN(sz) \
+  FB_GEN2(uint##sz##_t, sz) \
+  FB_GEN2(int##sz##_t, sz)
+
+class Endian {
+ public:
+  enum class Order : uint8_t {
+    LITTLE,
+    BIG
+  };
+
+  static constexpr Order order =
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    Order::LITTLE;
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    Order::BIG;
+#else
+# error Your machine uses a weird endianness!
+#endif  /* __BYTE_ORDER__ */
+
+  template <class T> static T swap(T x) {
+    return detail::EndianInt<T>::swap(x);
+  }
+  template <class T> static T big(T x) {
+    return detail::EndianInt<T>::big(x);
+  }
+  template <class T> static T little(T x) {
+    return detail::EndianInt<T>::little(x);
+  }
+
+  FB_GEN(64)
+  FB_GEN(32)
+  FB_GEN(16)
+  FB_GEN(8)
+};
+
+#undef FB_GEN
+#undef FB_GEN2
+#undef FB_GEN1
+
+/**
+ * Fast bit iteration facility.
+ */
+
+
+template <class BaseIter> class BitIterator;
+template <class BaseIter>
+BitIterator<BaseIter> findFirstSet(BitIterator<BaseIter>,
+                                   BitIterator<BaseIter>);
+/**
+ * Wrapper around an iterator over an integer type that iterates
+ * over its underlying bits in LSb to MSb order.
+ *
+ * BitIterator models the same iterator concepts as the base iterator.
+ */
+template <class BaseIter>
+class BitIterator
+  : public bititerator_detail::BitIteratorBase<BaseIter>::type {
+ public:
+  /**
+   * Return the number of bits in an element of the underlying iterator.
+   */
+  static unsigned int bitsPerBlock() {
+    return std::numeric_limits<
+      typename std::make_unsigned<
+        typename std::iterator_traits<BaseIter>::value_type
+      >::type
+    >::digits;
+  }
+
+  /**
+   * Construct a BitIterator that points at a given bit offset (default 0)
+   * in iter.
+   */
+  #pragma GCC diagnostic push // bitOffset shadows a member
+  #pragma GCC diagnostic ignored "-Wshadow"
+  explicit BitIterator(const BaseIter& iter, size_t bitOffset=0)
+    : bititerator_detail::BitIteratorBase<BaseIter>::type(iter),
+      bitOffset_(bitOffset) {
+    assert(bitOffset_ < bitsPerBlock());
+  }
+  #pragma GCC diagnostic pop
+
+  size_t bitOffset() const {
+    return bitOffset_;
+  }
+
+  void advanceToNextBlock() {
+    bitOffset_ = 0;
+    ++this->base_reference();
+  }
+
+  BitIterator& operator=(const BaseIter& other) {
+    this->~BitIterator();
+    new (this) BitIterator(other);
+    return *this;
+  }
+
+ private:
+  friend class boost::iterator_core_access;
+  friend BitIterator findFirstSet<>(BitIterator, BitIterator);
+
+  typedef bititerator_detail::BitReference<
+      typename std::iterator_traits<BaseIter>::reference,
+      typename std::iterator_traits<BaseIter>::value_type
+    > BitRef;
+
+  void advanceInBlock(size_t n) {
+    bitOffset_ += n;
+    assert(bitOffset_ < bitsPerBlock());
+  }
+
+  BitRef dereference() const {
+    return BitRef(*this->base_reference(), bitOffset_);
+  }
+
+  void advance(ssize_t n) {
+    size_t bpb = bitsPerBlock();
+    ssize_t blocks = n / bpb;
+    bitOffset_ += n % bpb;
+    if (bitOffset_ >= bpb) {
+      bitOffset_ -= bpb;
+      ++blocks;
+    }
+    this->base_reference() += blocks;
+  }
+
+  void increment() {
+    if (++bitOffset_ == bitsPerBlock()) {
+      advanceToNextBlock();
+    }
+  }
+
+  void decrement() {
+    if (bitOffset_-- == 0) {
+      bitOffset_ = bitsPerBlock() - 1;
+      --this->base_reference();
+    }
+  }
+
+  bool equal(const BitIterator& other) const {
+    return (bitOffset_ == other.bitOffset_ &&
+            this->base_reference() == other.base_reference());
+  }
+
+  ssize_t distance_to(const BitIterator& other) const {
+    return
+      (other.base_reference() - this->base_reference()) * bitsPerBlock() +
+      other.bitOffset_ - bitOffset_;
+  }
+
+  unsigned int bitOffset_;
+};
+
+/**
+ * Helper function, so you can write
+ * auto bi = makeBitIterator(container.begin());
+ */
+template <class BaseIter>
+BitIterator<BaseIter> makeBitIterator(const BaseIter& iter) {
+  return BitIterator<BaseIter>(iter);
+}
+
+
+/**
+ * Find first bit set in a range of bit iterators.
+ * 4.5x faster than the obvious std::find(begin, end, true);
+ */
+template <class BaseIter>
+BitIterator<BaseIter> findFirstSet(BitIterator<BaseIter> begin,
+                                   BitIterator<BaseIter> end) {
+  // shortcut to avoid ugly static_cast<>
+  static const typename BaseIter::value_type one = 1;
+
+  while (begin.base() != end.base()) {
+    typename BaseIter::value_type v = *begin.base();
+    // mask out the bits that don't matter (< begin.bitOffset)
+    v &= ~((one << begin.bitOffset()) - 1);
+    size_t firstSet = findFirstSet(v);
+    if (firstSet) {
+      --firstSet;  // now it's 0-based
+      assert(firstSet >= begin.bitOffset());
+      begin.advanceInBlock(firstSet - begin.bitOffset());
+      return begin;
+    }
+    begin.advanceToNextBlock();
+  }
+
+  // now begin points to the same block as end
+  if (end.bitOffset() != 0) {  // assume end is dereferenceable
+    typename BaseIter::value_type v = *begin.base();
+    // mask out the bits that don't matter (< begin.bitOffset)
+    v &= ~((one << begin.bitOffset()) - 1);
+    // mask out the bits that don't matter (>= end.bitOffset)
+    v &= (one << end.bitOffset()) - 1;
+    size_t firstSet = findFirstSet(v);
+    if (firstSet) {
+      --firstSet;  // now it's 0-based
+      assert(firstSet >= begin.bitOffset());
+      begin.advanceInBlock(firstSet - begin.bitOffset());
+      return begin;
+    }
+  }
+
+  return end;
+}
+
+
+template <class T, class Enable=void> struct Unaligned;
+
+/**
+ * Representation of an unaligned value of a POD type.
+ */
+FOLLY_PACK_PUSH
+template <class T>
+struct Unaligned<
+    T,
+    typename std::enable_if<std::is_pod<T>::value>::type> {
+  Unaligned() = default;  // uninitialized
+  /* implicit */ Unaligned(T v) : value(v) { }
+  T value;
+} FOLLY_PACK_ATTR;
+FOLLY_PACK_POP
+
+/**
+ * Read an unaligned value of type T and return it.
+ */
+template <class T>
+inline T loadUnaligned(const void* p) {
+  static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size");
+  static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment");
+  return static_cast<const Unaligned<T>*>(p)->value;
+}
+
+/**
+ * Write an unaligned value of type T.
+ */
+template <class T>
+inline void storeUnaligned(void* p, T value) {
+  static_assert(sizeof(Unaligned<T>) == sizeof(T), "Invalid unaligned size");
+  static_assert(alignof(Unaligned<T>) == 1, "Invalid alignment");
+  new (p) Unaligned<T>(value);
+}
+
+}  // namespace folly
+
+#endif /* FOLLY_BITS_H_ */
diff --git a/faux-folly/folly/CPortability.h b/faux-folly/folly/CPortability.h
new file mode 100644
index 0000000..65ffa6f
--- /dev/null
+++ b/faux-folly/folly/CPortability.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef CPORTABILITY_H
+#define CPORTABILITY_H
+
+/* These definitions are in a separate file so that they
+ * may be included from C- as well as C++-based projects. */
+
+/* Define a convenience macro to test when address sanitizer is being used
+ * across the different compilers (e.g. clang, gcc) */
+#if defined(__clang__)
+# if __has_feature(address_sanitizer)
+#  define FOLLY_SANITIZE_ADDRESS 1
+# endif
+#elif defined (__GNUC__) && \
+      (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ >= 5)) && \
+      __SANITIZE_ADDRESS__
+# define FOLLY_SANITIZE_ADDRESS 1
+#endif
+
+/* Define attribute wrapper for function attribute used to disable
+ * address sanitizer instrumentation. Unfortunately, this attribute
+ * has issues when inlining is used, so disable that as well. */
+#ifdef FOLLY_SANITIZE_ADDRESS
+# if defined(__clang__)
+#  if __has_attribute(__no_address_safety_analysis__)
+#   define FOLLY_DISABLE_ADDRESS_SANITIZER \
+      __attribute__((__no_address_safety_analysis__, __noinline__))
+#  elif __has_attribute(__no_sanitize_address__)
+#   define FOLLY_DISABLE_ADDRESS_SANITIZER \
+      __attribute__((__no_sanitize_address__, __noinline__))
+#  endif
+# elif defined(__GNUC__)
+#  define FOLLY_DISABLE_ADDRESS_SANITIZER \
+     __attribute__((__no_address_safety_analysis__, __noinline__))
+# endif
+#endif
+#ifndef FOLLY_DISABLE_ADDRESS_SANITIZER
+# define FOLLY_DISABLE_ADDRESS_SANITIZER
+#endif
+
+#endif
diff --git a/faux-folly/folly/Cancellation.h b/faux-folly/folly/Cancellation.h
new file mode 100644
index 0000000..3c9454a
--- /dev/null
+++ b/faux-folly/folly/Cancellation.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2015 Nest Labs, 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 <cassert>
+#include <mutex>
+#include <condition_variable>
+#include <memory>
+
+#ifdef __GNUC__
+#define FOLLY_CANCELLATION_WARN_UNUSED __attribute__((warn_unused_result))
+#else
+#define FOLLY_CANCELLATION_WARN_UNUSED
+#endif
+
+#include <folly/detail/CancellationDetail.h>
+
+namespace folly {
+
+/**
+ * RAII object to prevent a Cancellation state from changing
+ *
+ * CancellationStateLock shall not be re-lockable. This is because
+ * after you've unlocked it, it is possible that the underlying
+ * cancellation was cancelled. There isn't a reliable way to represent
+ * this other than throwing an exception.
+ *
+ * This object is convertable to bool. If it becomes `true', then
+ * Cancellation IS NOT cancelled and you may proceed as normal. If it
+ * becomes `false' then the Cancellation has been CANCELLED.
+ *
+ * <code>
+ *     auto lock = Cancellation.hold_state();
+ *     if (lock) {                         // i.e. static_cast<bool>(lock)
+ *         // NOT cancelled
+ *     } else {
+ *         // CANCELLED
+ *     }
+ * </code>
+ *
+ * This is implemented using the "try_lock()" idiom of
+ * std::unique_lock<T>.  Thus operator bool() is implemented to return
+ * 'true' if the lock is acquired, and 'false' if not. In the case of
+ * a Cancellation object, returning `true' means that we've acquired a
+ * hold on the "un-cancelled" state. `false' means that the
+ * Cancellation has been cancelled.
+ */
+class CancellationStateLock : public std::unique_lock<detail::CancellationSharedState>
+{
+public:
+    friend class Cancellation;
+
+    using base_type = std::unique_lock<detail::CancellationSharedState>;
+
+    /* Should only be constructed in Cancellation::hold_state() */
+    CancellationStateLock() = delete;
+
+    ~CancellationStateLock() = default;
+
+    /* non-copyable */
+    CancellationStateLock(const CancellationStateLock& o) = delete;
+    CancellationStateLock& operator= (const CancellationStateLock& o) = delete;
+
+    /**
+     * move constructor
+     */
+    CancellationStateLock(CancellationStateLock&& o) :
+        base_type(std::forward<base_type>(o))
+    {}
+
+    /**
+     * move operator
+     */
+    CancellationStateLock& operator= (CancellationStateLock&& o)
+    {
+        base_type::operator=(std::forward<base_type>(o));
+        return *this;
+    }
+
+private:
+    /**
+     * Construct a lock that refers to a specific CancellationSharedState
+     *
+     */
+    CancellationStateLock(detail::CancellationSharedState& ss, std::try_to_lock_t tag)
+        : base_type(ss, tag)
+    {
+    }
+
+    /**
+     * This is made private to prevent its use (i.e. for re-lock()).
+     */
+    void lock();
+};
+
+/**
+ * A thread-safe Cancellation object
+ *
+ * This is a token that is used to indicate whether or not some
+ * operation or object is cancelled, deleted, out of date,
+ * whatever. Like a mutex, this is an advisory token, and therefore it
+ * is up to the programmer to use it properly.
+ *
+ * It is implemented so that all objects refer to a shared state (much
+ * like a shared_ptr). Thus, all copies of the object refer to the
+ * same shared state object. Therefore a Cancellation token can be
+ * freely copied (by value), retaining a reference to the same shared
+ * state as the copied-from token.
+ *
+ * It has a very simple state machine:
+ *
+ * <code>
+ *                               cancel()
+ * start --> [NOT CANCELLED] ----------------> [CANCELLED]
+ * </code>
+ *
+ * The CANCELLED state is terminal.
+ */
+class Cancellation
+{
+public:
+    /**
+     * Construct a new cancellation object.
+     */
+    Cancellation() :
+        _d(new detail::CancellationSharedState)
+    {}
+
+    /**
+     * Copy constructor
+     *
+     * The new Cancellation will refer to the same shared state as `o'.
+     *
+     * @param o an existing Cancellation object from whom we get our
+     * shared state.
+     */
+    Cancellation(const Cancellation& o) :
+        _d(o._d)
+    {}
+
+    /**
+     * Assignment operator
+     *
+     * On return, the Cancellation will refer to the same shared state as `o'.
+     * We will drop the reference to the `current' shared state.
+     *
+     * @param o an existing Cancellation object from whom we get our
+     * shared state.
+     **/
+    Cancellation& operator=(const Cancellation& o)
+    {
+        _d = o._d;
+        return *this;
+    }
+
+    /**
+     * Move constructor
+     *
+     * The new Cancellation will refer to the same shared state as `o'.
+     *
+     * @param o an existing Cancellation object from whom we get our
+     * shared state.
+     */
+    Cancellation(Cancellation&& o) :
+        _d(std::move(o._d))
+    {}
+
+    /**
+     * Move operator
+     *
+     * On return, the new Cancellation will refer to the same shared
+     * state as `o'. We will drop the reference to the `current' shared state.
+     *
+     * @param o an existing Cancellation object from whom we get our
+     * shared state.
+     */
+    Cancellation& operator=(Cancellation&& o)
+    {
+        _d = std::move(o._d);
+        return *this;
+    }
+
+    /**
+     * Returns the current cancellation state of the object.
+     *
+     * It is generally not recommended that you use this function
+     * because it is not thread-safe. Use hold_state(), instead.
+     *
+     * Once an object is Cancelled, this function returns `true'. Once
+     * that happens, this function will never again return `false'.
+     *
+     * However, if this function returns `false' -- there's a
+     * possibility that the object is actually Cancelled. Therefore
+     * you should never rely on this value.
+     *
+     * @return true if the object has been cancelled. false if the
+     * object has NOT been cancelled.
+     */
+    bool is_cancelled() const
+    {
+        return _d->is_cancelled();
+    }
+
+    /**
+     * Set the state of the object to "cancelled"
+     *
+     * If the object is alread cancelled, this function returns
+     * immediately.
+     *
+     * If not already cancelled, this function may block until all
+     * state holds are released.
+     */
+    void cancel()
+    {
+        _d->cancel();
+    }
+
+    /**
+     * Acquire a hold on the state of the object
+     *
+     * If the object is not not cancelled, the returned object will
+     * prevent cancellation until it is deleted or released
+     * (via. unlock()).
+     *
+     * If the object is cancelled, it returns immediately.
+     *
+     * You MUST check the validity of the returned lock. When cast to
+     * bool, if it returns false then the object is cancelled.
+     *
+     * @return a CancellationStateLock object the prevents the state
+     * of the object from changing.
+     */
+    CancellationStateLock hold_state() const FOLLY_CANCELLATION_WARN_UNUSED {
+        CancellationStateLock lock(*_d, std::try_to_lock);
+        return lock;
+    }
+
+private:
+    std::shared_ptr<detail::CancellationSharedState> _d;
+};
+
+} /* namespace folly */
diff --git a/faux-folly/folly/ContainerTraits.h b/faux-folly/folly/ContainerTraits.h
new file mode 100644
index 0000000..5f6e5d4
--- /dev/null
+++ b/faux-folly/folly/ContainerTraits.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BASE_CONTAINER_TRAITS_H_
+#define FOLLY_BASE_CONTAINER_TRAITS_H_
+
+#include <folly/Traits.h>
+
+namespace folly {
+
+FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(container_emplace_back_traits, emplace_back);
+
+template <class Container, typename... Args>
+inline
+typename std::enable_if<
+    container_emplace_back_traits<Container, void(Args...)>::value>::type
+container_emplace_back_or_push_back(Container& container, Args&&... args) {
+  container.emplace_back(std::forward<Args>(args)...);
+}
+
+template <class Container, typename... Args>
+inline
+typename std::enable_if<
+    !container_emplace_back_traits<Container, void(Args...)>::value>::type
+container_emplace_back_or_push_back(Container& container, Args&&... args) {
+  using v = typename Container::value_type;
+  container.push_back(v(std::forward<Args>(args)...));
+}
+
+}
+
+#endif
diff --git a/faux-folly/folly/Conv.cpp b/faux-folly/folly/Conv.cpp
new file mode 100644
index 0000000..0ae9b22
--- /dev/null
+++ b/faux-folly/folly/Conv.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2015 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.
+ */
+#define FOLLY_CONV_INTERNAL
+#include <folly/Conv.h>
+
+namespace folly {
+namespace detail {
+
+extern const char digit1[101] =
+  "00000000001111111111222222222233333333334444444444"
+  "55555555556666666666777777777788888888889999999999";
+extern const char digit2[101] =
+  "01234567890123456789012345678901234567890123456789"
+  "01234567890123456789012345678901234567890123456789";
+
+template <> const char *const MaxString<bool>::value = "true";
+template <> const char *const MaxString<uint8_t>::value = "255";
+template <> const char *const MaxString<uint16_t>::value = "65535";
+template <> const char *const MaxString<uint32_t>::value = "4294967295";
+#if __SIZEOF_LONG__ == 4
+template <> const char *const MaxString<unsigned long>::value =
+  "4294967295";
+#else
+template <> const char *const MaxString<unsigned long>::value =
+  "18446744073709551615";
+#endif
+static_assert(sizeof(unsigned long) >= 4,
+              "Wrong value for MaxString<unsigned long>::value,"
+              " please update.");
+template <> const char *const MaxString<unsigned long long>::value =
+  "18446744073709551615";
+static_assert(sizeof(unsigned long long) >= 8,
+              "Wrong value for MaxString<unsigned long long>::value"
+              ", please update.");
+
+#ifdef FOLLY_HAVE_INT128_T
+template <> const char *const MaxString<__uint128_t>::value =
+  "340282366920938463463374607431768211455";
+#endif
+
+inline bool bool_str_cmp(const char** b, size_t len, const char* value) {
+  // Can't use strncasecmp, since we want to ensure that the full value matches
+  const char* p = *b;
+  const char* e = *b + len;
+  const char* v = value;
+  while (*v != '\0') {
+    if (p == e || tolower(*p) != *v) { // value is already lowercase
+      return false;
+    }
+    ++p;
+    ++v;
+  }
+
+  *b = p;
+  return true;
+}
+
+bool str_to_bool(StringPiece* src) {
+  auto b = src->begin(), e = src->end();
+  for (;; ++b) {
+    FOLLY_RANGE_CHECK_STRINGPIECE(
+      b < e, "No non-whitespace characters found in input string", *src);
+    if (!isspace(*b)) break;
+  }
+
+  bool result;
+  size_t len = e - b;
+  switch (*b) {
+    case '0':
+    case '1': {
+      result = false;
+      for (; b < e && isdigit(*b); ++b) {
+        FOLLY_RANGE_CHECK_STRINGPIECE(
+          !result && (*b == '0' || *b == '1'),
+          "Integer overflow when parsing bool: must be 0 or 1", *src);
+        result = (*b == '1');
+      }
+      break;
+    }
+    case 'y':
+    case 'Y':
+      result = true;
+      if (!bool_str_cmp(&b, len, "yes")) {
+        ++b;  // accept the single 'y' character
+      }
+      break;
+    case 'n':
+    case 'N':
+      result = false;
+      if (!bool_str_cmp(&b, len, "no")) {
+        ++b;
+      }
+      break;
+    case 't':
+    case 'T':
+      result = true;
+      if (!bool_str_cmp(&b, len, "true")) {
+        ++b;
+      }
+      break;
+    case 'f':
+    case 'F':
+      result = false;
+      if (!bool_str_cmp(&b, len, "false")) {
+        ++b;
+      }
+      break;
+    case 'o':
+    case 'O':
+      if (bool_str_cmp(&b, len, "on")) {
+        result = true;
+      } else if (bool_str_cmp(&b, len, "off")) {
+        result = false;
+      } else {
+        FOLLY_RANGE_CHECK_STRINGPIECE(false, "Invalid value for bool", *src);
+      }
+      break;
+    default:
+      FOLLY_RANGE_CHECK_STRINGPIECE(false, "Invalid value for bool", *src);
+  }
+
+  src->assign(b, e);
+  return result;
+}
+
+} // namespace detail
+} // namespace folly
diff --git a/faux-folly/folly/Conv.h b/faux-folly/folly/Conv.h
new file mode 100644
index 0000000..74cdf51
--- /dev/null
+++ b/faux-folly/folly/Conv.h
@@ -0,0 +1,1565 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Converts anything to anything, with an emphasis on performance and
+ * safety.
+ *
+ * @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
+ */
+
+#ifndef FOLLY_BASE_CONV_H_
+#define FOLLY_BASE_CONV_H_
+
+#include <folly/FBString.h>
+#include <folly/Likely.h>
+#include <folly/Preprocessor.h>
+#include <folly/Range.h>
+
+#include <boost/implicit_cast.hpp>
+#include <algorithm>
+#include <type_traits>
+#include <limits>
+#include <string>
+#include <tuple>
+#include <stdexcept>
+#include <typeinfo>
+
+#include <limits.h>
+
+// V8 JavaScript implementation
+#include <double-conversion/double-conversion.h>
+
+#define FOLLY_RANGE_CHECK_STRINGIZE(x) #x
+#define FOLLY_RANGE_CHECK_STRINGIZE2(x) FOLLY_RANGE_CHECK_STRINGIZE(x)
+
+// Android doesn't support std::to_string so just use a placeholder there.
+#ifdef __ANDROID__
+#define FOLLY_RANGE_CHECK_TO_STRING(x) std::string("N/A")
+#else
+#define FOLLY_RANGE_CHECK_TO_STRING(x) std::to_string(x)
+#endif
+
+#define FOLLY_RANGE_CHECK(condition, message, src)                          \
+  ((condition) ? (void)0 : throw std::range_error(                          \
+    (std::string(__FILE__ "(" FOLLY_RANGE_CHECK_STRINGIZE2(__LINE__) "): ") \
+     + (message) + ": '" + (src) + "'").c_str()))
+
+#define FOLLY_RANGE_CHECK_BEGIN_END(condition, message, b, e)    \
+  FOLLY_RANGE_CHECK(condition, message, std::string((b), (e) - (b)))
+
+#define FOLLY_RANGE_CHECK_STRINGPIECE(condition, message, sp)    \
+  FOLLY_RANGE_CHECK(condition, message, std::string((sp).data(), (sp).size()))
+
+namespace folly {
+
+/**
+ * The identity conversion function.
+ * to<T>(T) returns itself for all types T.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<std::is_same<Tgt, Src>::value, Tgt>::type
+to(const Src & value) {
+  return value;
+}
+
+template <class Tgt, class Src>
+typename std::enable_if<std::is_same<Tgt, Src>::value, Tgt>::type
+to(Src && value) {
+  return std::move(value);
+}
+
+/*******************************************************************************
+ * Integral to integral
+ ******************************************************************************/
+
+/**
+ * Checked conversion from integral to integral. The checks are only
+ * performed when meaningful, e.g. conversion from int to long goes
+ * unchecked.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value
+  && std::is_integral<Tgt>::value
+  && !std::is_same<Tgt, Src>::value,
+  Tgt>::type
+to(const Src & value) {
+  /* static */ if (std::numeric_limits<Tgt>::max()
+                   < std::numeric_limits<Src>::max()) {
+    FOLLY_RANGE_CHECK(
+      (!greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)),
+      "Overflow",
+      FOLLY_RANGE_CHECK_TO_STRING(value));
+  }
+  /* static */ if (std::is_signed<Src>::value &&
+                   (!std::is_signed<Tgt>::value || sizeof(Src) > sizeof(Tgt))) {
+    FOLLY_RANGE_CHECK(
+      (!less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)),
+      "Negative overflow",
+      FOLLY_RANGE_CHECK_TO_STRING(value));
+  }
+  return static_cast<Tgt>(value);
+}
+
+/*******************************************************************************
+ * Floating point to floating point
+ ******************************************************************************/
+
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_floating_point<Tgt>::value
+  && std::is_floating_point<Src>::value
+  && !std::is_same<Tgt, Src>::value,
+  Tgt>::type
+to(const Src & value) {
+  /* static */ if (std::numeric_limits<Tgt>::max() <
+                   std::numeric_limits<Src>::max()) {
+    FOLLY_RANGE_CHECK(value <= std::numeric_limits<Tgt>::max(),
+                      "Overflow",
+                      FOLLY_RANGE_CHECK_TO_STRING(value));
+    FOLLY_RANGE_CHECK(value >= -std::numeric_limits<Tgt>::max(),
+                      "Negative overflow",
+                      FOLLY_RANGE_CHECK_TO_STRING(value));
+  }
+  return boost::implicit_cast<Tgt>(value);
+}
+
+/*******************************************************************************
+ * Anything to string
+ ******************************************************************************/
+
+namespace detail {
+
+template <class T>
+const T& getLastElement(const T & v) {
+  return v;
+}
+
+template <class T, class... Ts>
+typename std::tuple_element<
+  sizeof...(Ts),
+  std::tuple<T, Ts...> >::type const&
+  getLastElement(const T&, const Ts&... vs) {
+  return getLastElement(vs...);
+}
+
+// This class exists to specialize away std::tuple_element in the case where we
+// have 0 template arguments. Without this, Clang/libc++ will blow a
+// static_assert even if tuple_element is protected by an enable_if.
+template <class... Ts>
+struct last_element {
+  typedef typename std::enable_if<
+    sizeof...(Ts) >= 1,
+    typename std::tuple_element<
+      sizeof...(Ts) - 1, std::tuple<Ts...>
+    >::type>::type type;
+};
+
+template <>
+struct last_element<> {
+  typedef void type;
+};
+
+} // namespace detail
+
+/*******************************************************************************
+ * Conversions from integral types to string types.
+ ******************************************************************************/
+
+#if FOLLY_HAVE_INT128_T
+namespace detail {
+
+template <typename IntegerType>
+constexpr unsigned int
+digitsEnough() {
+  return ceil((double(sizeof(IntegerType) * CHAR_BIT) * M_LN2) / M_LN10);
+}
+
+inline size_t
+unsafeTelescope128(char * buffer, size_t room, unsigned __int128 x) {
+  typedef unsigned __int128 Usrc;
+  size_t p = room - 1;
+
+  while (x >= (Usrc(1) << 64)) { // Using 128-bit division while needed
+    const auto y = x / 10;
+    const auto digit = x % 10;
+
+    buffer[p--] = '0' + digit;
+    x = y;
+  }
+
+  uint64_t xx = x; // Moving to faster 64-bit division thereafter
+
+  while (xx >= 10) {
+    const auto y = xx / 10ULL;
+    const auto digit = xx % 10ULL;
+
+    buffer[p--] = '0' + digit;
+    xx = y;
+  }
+
+  buffer[p] = '0' + xx;
+
+  return p;
+}
+
+}
+#endif
+
+/**
+ * Returns the number of digits in the base 10 representation of an
+ * uint64_t. Useful for preallocating buffers and such. It's also used
+ * internally, see below. Measurements suggest that defining a
+ * separate overload for 32-bit integers is not worthwhile.
+ */
+
+inline uint32_t digits10(uint64_t v) {
+#ifdef __x86_64__
+
+  // For this arch we can get a little help from specialized CPU instructions
+  // which can count leading zeroes; 64 minus that is appx. log (base 2).
+  // Use that to approximate base-10 digits (log_10) and then adjust if needed.
+
+  // 10^i, defined for i 0 through 19.
+  // This is 20 * 8 == 160 bytes, which fits neatly into 5 cache lines
+  // (assuming a cache line size of 64).
+  static const uint64_t powersOf10[20] FOLLY_ALIGNED(64) = {
+    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
+    10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000,
+    1000000000000000, 10000000000000000, 100000000000000000,
+    1000000000000000000, 10000000000000000000UL
+  };
+
+  // "count leading zeroes" operation not valid; for 0; special case this.
+  if UNLIKELY (! v) {
+    return 1;
+  }
+
+  // bits is in the ballpark of log_2(v).
+  const uint8_t leadingZeroes = __builtin_clzll(v);
+  const auto bits = 63 - leadingZeroes;
+
+  // approximate log_10(v) == log_10(2) * bits.
+  // Integer magic below: 77/256 is appx. 0.3010 (log_10(2)).
+  // The +1 is to make this the ceiling of the log_10 estimate.
+  const uint32_t minLength = 1 + ((bits * 77) >> 8);
+
+  // return that log_10 lower bound, plus adjust if input >= 10^(that bound)
+  // in case there's a small error and we misjudged length.
+  return minLength + (uint32_t) (UNLIKELY (v >= powersOf10[minLength]));
+
+#else
+
+  uint32_t result = 1;
+  for (;;) {
+    if (LIKELY(v < 10)) return result;
+    if (LIKELY(v < 100)) return result + 1;
+    if (LIKELY(v < 1000)) return result + 2;
+    if (LIKELY(v < 10000)) return result + 3;
+    // Skip ahead by 4 orders of magnitude
+    v /= 10000U;
+    result += 4;
+  }
+
+#endif
+}
+
+/**
+ * Copies the ASCII base 10 representation of v into buffer and
+ * returns the number of bytes written. Does NOT append a \0. Assumes
+ * the buffer points to digits10(v) bytes of valid memory. Note that
+ * uint64 needs at most 20 bytes, uint32_t needs at most 10 bytes,
+ * uint16_t needs at most 5 bytes, and so on. Measurements suggest
+ * that defining a separate overload for 32-bit integers is not
+ * worthwhile.
+ *
+ * This primitive is unsafe because it makes the size assumption and
+ * because it does not add a terminating \0.
+ */
+
+inline uint32_t uint64ToBufferUnsafe(uint64_t v, char *const buffer) {
+  auto const result = digits10(v);
+  // WARNING: using size_t or pointer arithmetic for pos slows down
+  // the loop below 20x. This is because several 32-bit ops can be
+  // done in parallel, but only fewer 64-bit ones.
+  uint32_t pos = result - 1;
+  while (v >= 10) {
+    // Keep these together so a peephole optimization "sees" them and
+    // computes them in one shot.
+    auto const q = v / 10;
+    auto const r = static_cast<uint32_t>(v % 10);
+    buffer[pos--] = '0' + r;
+    v = q;
+  }
+  // Last digit is trivial to handle
+  buffer[pos] = static_cast<uint32_t>(v) + '0';
+  return result;
+}
+
+/**
+ * A single char gets appended.
+ */
+template <class Tgt>
+void toAppend(char value, Tgt * result) {
+  *result += value;
+}
+
+template<class T>
+constexpr typename std::enable_if<
+  std::is_same<T, char>::value,
+  size_t>::type
+estimateSpaceNeeded(T) {
+  return 1;
+}
+
+/**
+ * Ubiquitous helper template for writing string appenders
+ */
+template <class T> struct IsSomeString {
+  enum { value = std::is_same<T, std::string>::value
+         || std::is_same<T, fbstring>::value };
+};
+
+/**
+ * Everything implicitly convertible to const char* gets appended.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_convertible<Src, const char*>::value
+  && IsSomeString<Tgt>::value>::type
+toAppend(Src value, Tgt * result) {
+  // Treat null pointers like an empty string, as in:
+  // operator<<(std::ostream&, const char*).
+  const char* c = value;
+  if (c) {
+    result->append(value);
+  }
+}
+
+template<class Src>
+typename std::enable_if<
+  std::is_convertible<Src, const char*>::value,
+  size_t>::type
+estimateSpaceNeeded(Src value) {
+  const char *c = value;
+  if (c) {
+    return folly::StringPiece(value).size();
+  };
+  return 0;
+}
+
+template<class Src>
+typename std::enable_if<
+  (std::is_convertible<Src, folly::StringPiece>::value ||
+  IsSomeString<Src>::value) &&
+  !std::is_convertible<Src, const char*>::value,
+  size_t>::type
+estimateSpaceNeeded(Src value) {
+  return folly::StringPiece(value).size();
+}
+
+template<class Src>
+typename std::enable_if<
+  std::is_pointer<Src>::value &&
+  IsSomeString<std::remove_pointer<Src>>::value,
+  size_t>::type
+estimateSpaceNeeded(Src value) {
+  return value->size();
+}
+
+/**
+ * Strings get appended, too.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  IsSomeString<Src>::value && IsSomeString<Tgt>::value>::type
+toAppend(const Src& value, Tgt * result) {
+  result->append(value);
+}
+
+/**
+ * and StringPiece objects too
+ */
+template <class Tgt>
+typename std::enable_if<
+   IsSomeString<Tgt>::value>::type
+toAppend(StringPiece value, Tgt * result) {
+  result->append(value.data(), value.size());
+}
+
+/**
+ * There's no implicit conversion from fbstring to other string types,
+ * so make a specialization.
+ */
+template <class Tgt>
+typename std::enable_if<
+   IsSomeString<Tgt>::value>::type
+toAppend(const fbstring& value, Tgt * result) {
+  result->append(value.data(), value.size());
+}
+
+#if FOLLY_HAVE_INT128_T
+/**
+ * Special handling for 128 bit integers.
+ */
+
+template <class Tgt>
+void
+toAppend(__int128 value, Tgt * result) {
+  typedef unsigned __int128 Usrc;
+  char buffer[detail::digitsEnough<unsigned __int128>() + 1];
+  size_t p;
+
+  if (value < 0) {
+    p = detail::unsafeTelescope128(buffer, sizeof(buffer), Usrc(-value));
+    buffer[--p] = '-';
+  } else {
+    p = detail::unsafeTelescope128(buffer, sizeof(buffer), value);
+  }
+
+  result->append(buffer + p, buffer + sizeof(buffer));
+}
+
+template <class Tgt>
+void
+toAppend(unsigned __int128 value, Tgt * result) {
+  char buffer[detail::digitsEnough<unsigned __int128>()];
+  size_t p;
+
+  p = detail::unsafeTelescope128(buffer, sizeof(buffer), value);
+
+  result->append(buffer + p, buffer + sizeof(buffer));
+}
+
+template<class T>
+constexpr typename std::enable_if<
+  std::is_same<T, __int128>::value,
+  size_t>::type
+estimateSpaceNeeded(T) {
+  return detail::digitsEnough<__int128>();
+}
+
+template<class T>
+constexpr typename std::enable_if<
+  std::is_same<T, unsigned __int128>::value,
+  size_t>::type
+estimateSpaceNeeded(T) {
+  return detail::digitsEnough<unsigned __int128>();
+}
+
+#endif
+
+/**
+ * int32_t and int64_t to string (by appending) go through here. The
+ * result is APPENDED to a preexisting string passed as the second
+ * parameter. This should be efficient with fbstring because fbstring
+ * incurs no dynamic allocation below 23 bytes and no number has more
+ * than 22 bytes in its textual representation (20 for digits, one for
+ * sign, one for the terminating 0).
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value && std::is_signed<Src>::value &&
+  IsSomeString<Tgt>::value && sizeof(Src) >= 4>::type
+toAppend(Src value, Tgt * result) {
+  char buffer[20];
+  if (value < 0) {
+    result->push_back('-');
+    result->append(buffer, uint64ToBufferUnsafe(-uint64_t(value), buffer));
+  } else {
+    result->append(buffer, uint64ToBufferUnsafe(value, buffer));
+  }
+}
+
+template <class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value && std::is_signed<Src>::value
+  && sizeof(Src) >= 4 && sizeof(Src) < 16,
+  size_t>::type
+estimateSpaceNeeded(Src value) {
+  if (value < 0) {
+    return 1 + digits10(static_cast<uint64_t>(-value));
+  }
+
+  return digits10(static_cast<uint64_t>(value));
+}
+
+/**
+ * As above, but for uint32_t and uint64_t.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value && !std::is_signed<Src>::value
+  && IsSomeString<Tgt>::value && sizeof(Src) >= 4>::type
+toAppend(Src value, Tgt * result) {
+  char buffer[20];
+  result->append(buffer, buffer + uint64ToBufferUnsafe(value, buffer));
+}
+
+template <class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value && !std::is_signed<Src>::value
+  && sizeof(Src) >= 4 && sizeof(Src) < 16,
+  size_t>::type
+estimateSpaceNeeded(Src value) {
+  return digits10(value);
+}
+
+/**
+ * All small signed and unsigned integers to string go through 32-bit
+ * types int32_t and uint32_t, respectively.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value
+  && IsSomeString<Tgt>::value && sizeof(Src) < 4>::type
+toAppend(Src value, Tgt * result) {
+  typedef typename
+    std::conditional<std::is_signed<Src>::value, int64_t, uint64_t>::type
+    Intermediate;
+  toAppend<Tgt>(static_cast<Intermediate>(value), result);
+}
+
+template <class Src>
+typename std::enable_if<
+  std::is_integral<Src>::value
+  && sizeof(Src) < 4
+  && !std::is_same<Src, char>::value,
+  size_t>::type
+estimateSpaceNeeded(Src value) {
+  typedef typename
+    std::conditional<std::is_signed<Src>::value, int64_t, uint64_t>::type
+    Intermediate;
+  return estimateSpaceNeeded(static_cast<Intermediate>(value));
+}
+
+#if defined(__clang__) || __GNUC_PREREQ(4, 7)
+// std::underlying_type became available by gcc 4.7.0
+
+/**
+ * Enumerated values get appended as integers.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value && IsSomeString<Tgt>::value>::type
+toAppend(Src value, Tgt * result) {
+  toAppend(
+      static_cast<typename std::underlying_type<Src>::type>(value), result);
+}
+
+template <class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value, size_t>::type
+estimateSpaceNeeded(Src value) {
+  return estimateSpaceNeeded(
+      static_cast<typename std::underlying_type<Src>::type>(value));
+}
+
+#else
+
+/**
+ * Enumerated values get appended as integers.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value && IsSomeString<Tgt>::value>::type
+toAppend(Src value, Tgt * result) {
+  /* static */ if (Src(-1) < 0) {
+    /* static */ if (sizeof(Src) <= sizeof(int)) {
+      toAppend(static_cast<int>(value), result);
+    } else {
+      toAppend(static_cast<long>(value), result);
+    }
+  } else {
+    /* static */ if (sizeof(Src) <= sizeof(int)) {
+      toAppend(static_cast<unsigned int>(value), result);
+    } else {
+      toAppend(static_cast<unsigned long>(value), result);
+    }
+  }
+}
+
+template <class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value, size_t>::type
+estimateSpaceNeeded(Src value) {
+  /* static */ if (Src(-1) < 0) {
+    /* static */ if (sizeof(Src) <= sizeof(int)) {
+      return estimateSpaceNeeded(static_cast<int>(value));
+    } else {
+      return estimateSpaceNeeded(static_cast<long>(value));
+    }
+  } else {
+    /* static */ if (sizeof(Src) <= sizeof(int)) {
+      return estimateSpaceNeeded(static_cast<unsigned int>(value));
+    } else {
+      return estimateSpaceNeeded(static_cast<unsigned long>(value));
+    }
+  }
+}
+
+#endif // gcc 4.7 onwards
+
+/*******************************************************************************
+ * Conversions from floating-point types to string types.
+ ******************************************************************************/
+
+namespace detail {
+constexpr int kConvMaxDecimalInShortestLow = -6;
+constexpr int kConvMaxDecimalInShortestHigh = 21;
+} // folly::detail
+
+/** Wrapper around DoubleToStringConverter **/
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_floating_point<Src>::value
+  && IsSomeString<Tgt>::value>::type
+toAppend(
+  Src value,
+  Tgt * result,
+  double_conversion::DoubleToStringConverter::DtoaMode mode,
+  unsigned int numDigits) {
+  using namespace double_conversion;
+  DoubleToStringConverter
+    conv(DoubleToStringConverter::NO_FLAGS,
+         "Infinity", "NaN", 'E',
+         detail::kConvMaxDecimalInShortestLow,
+         detail::kConvMaxDecimalInShortestHigh,
+         6,   // max leading padding zeros
+         1);  // max trailing padding zeros
+  char buffer[256];
+  StringBuilder builder(buffer, sizeof(buffer));
+  switch (mode) {
+    case DoubleToStringConverter::SHORTEST:
+      conv.ToShortest(value, &builder);
+      break;
+    case DoubleToStringConverter::FIXED:
+      conv.ToFixed(value, numDigits, &builder);
+      break;
+    default:
+      CHECK(mode == DoubleToStringConverter::PRECISION);
+      conv.ToPrecision(value, numDigits, &builder);
+      break;
+  }
+  const size_t length = builder.position();
+  builder.Finalize();
+  result->append(buffer, length);
+}
+
+/**
+ * As above, but for floating point
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_floating_point<Src>::value
+  && IsSomeString<Tgt>::value>::type
+toAppend(Src value, Tgt * result) {
+  toAppend(
+    value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0);
+}
+
+/**
+ * Upper bound of the length of the output from
+ * DoubleToStringConverter::ToShortest(double, StringBuilder*),
+ * as used in toAppend(double, string*).
+ */
+template <class Src>
+typename std::enable_if<
+  std::is_floating_point<Src>::value, size_t>::type
+estimateSpaceNeeded(Src value) {
+  // kBase10MaximalLength is 17. We add 1 for decimal point,
+  // e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point.
+  constexpr int kMaxMantissaSpace =
+    double_conversion::DoubleToStringConverter::kBase10MaximalLength + 1;
+  // strlen("E-") + digits10(numeric_limits<double>::max_exponent10)
+  constexpr int kMaxExponentSpace = 2 + 3;
+  static const int kMaxPositiveSpace = std::max({
+      // E.g. 1.1111111111111111E-100.
+      kMaxMantissaSpace + kMaxExponentSpace,
+      // E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6.
+      kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow,
+      // If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest
+      // number > 1 which ToShortest outputs in exponential notation,
+      // so 21 is the longest non-exponential number > 1.
+      detail::kConvMaxDecimalInShortestHigh
+    });
+  return kMaxPositiveSpace + (value < 0);  // +1 for minus sign, if negative
+}
+
+/**
+ * This can be specialized, together with adding specialization
+ * for estimateSpaceNeed for your type, so that we allocate
+ * as much as you need instead of the default
+ */
+template<class Src>
+struct HasLengthEstimator : std::false_type {};
+
+template <class Src>
+constexpr typename std::enable_if<
+  !std::is_fundamental<Src>::value
+#ifdef FOLLY_HAVE_INT128_T
+  // On OSX 10.10, is_fundamental<__int128> is false :-O
+  && !std::is_same<__int128, Src>::value
+  && !std::is_same<unsigned __int128, Src>::value
+#endif
+  && !IsSomeString<Src>::value
+  && !std::is_convertible<Src, const char*>::value
+  && !std::is_convertible<Src, StringPiece>::value
+  && !std::is_enum<Src>::value
+  && !HasLengthEstimator<Src>::value,
+  size_t>::type
+estimateSpaceNeeded(const Src&) {
+  return sizeof(Src) + 1; // dumbest best effort ever?
+}
+
+namespace detail {
+
+inline size_t estimateSpaceToReserve(size_t sofar) {
+  return sofar;
+}
+
+template <class T, class... Ts>
+size_t estimateSpaceToReserve(size_t sofar, const T& v, const Ts&... vs) {
+  return estimateSpaceToReserve(sofar + estimateSpaceNeeded(v), vs...);
+}
+
+template<class T>
+size_t estimateSpaceToReserve(size_t sofar, const T& v) {
+  return sofar + estimateSpaceNeeded(v);
+}
+
+template<class...Ts>
+void reserveInTarget(const Ts&...vs) {
+  getLastElement(vs...)->reserve(estimateSpaceToReserve(0, vs...));
+}
+
+template<class Delimiter, class...Ts>
+void reserveInTargetDelim(const Delimiter& d, const Ts&...vs) {
+  static_assert(sizeof...(vs) >= 2, "Needs at least 2 args");
+  size_t fordelim = (sizeof...(vs) - 2) * estimateSpaceToReserve(0, d);
+  getLastElement(vs...)->reserve(estimateSpaceToReserve(fordelim, vs...));
+}
+
+/**
+ * Variadic base case: append one element
+ */
+template <class T, class Tgt>
+typename std::enable_if<
+  IsSomeString<typename std::remove_pointer<Tgt>::type>
+  ::value>::type
+toAppendStrImpl(const T& v, Tgt result) {
+  toAppend(v, result);
+}
+
+template <class T, class... Ts>
+typename std::enable_if<sizeof...(Ts) >= 2
+  && IsSomeString<
+  typename std::remove_pointer<
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
+toAppendStrImpl(const T& v, const Ts&... vs) {
+  toAppend(v, getLastElement(vs...));
+  toAppendStrImpl(vs...);
+}
+
+template <class Delimiter, class T, class Tgt>
+typename std::enable_if<
+  IsSomeString<typename std::remove_pointer<Tgt>::type>
+  ::value>::type
+toAppendDelimStrImpl(const Delimiter& delim, const T& v, Tgt result) {
+  toAppend(v, result);
+}
+
+template <class Delimiter, class T, class... Ts>
+typename std::enable_if<sizeof...(Ts) >= 2
+  && IsSomeString<
+  typename std::remove_pointer<
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
+toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) {
+  // we are really careful here, calling toAppend with just one element does
+  // not try to estimate space needed (as we already did that). If we call
+  // toAppend(v, delim, ....) we would do unnecesary size calculation
+  toAppend(v, detail::getLastElement(vs...));
+  toAppend(delim, detail::getLastElement(vs...));
+  toAppendDelimStrImpl(delim, vs...);
+}
+} // folly::detail
+
+
+/**
+ * Variadic conversion to string. Appends each element in turn.
+ * If we have two or more things to append, we it will not reserve
+ * the space for them and will depend on strings exponential growth.
+ * If you just append once consider using toAppendFit which reserves
+ * the space needed (but does not have exponential as a result).
+ */
+template <class... Ts>
+typename std::enable_if<sizeof...(Ts) >= 3
+  && IsSomeString<
+  typename std::remove_pointer<
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
+toAppend(const Ts&... vs) {
+  ::folly::detail::toAppendStrImpl(vs...);
+}
+
+/**
+ * Special version of the call that preallocates exaclty as much memory
+ * as need for arguments to be stored in target. This means we are
+ * not doing exponential growth when we append. If you are using it
+ * in a loop you are aiming at your foot with a big perf-destroying
+ * bazooka.
+ * On the other hand if you are appending to a string once, this
+ * will probably save a few calls to malloc.
+ */
+template <class... Ts>
+typename std::enable_if<
+  IsSomeString<
+  typename std::remove_pointer<
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
+toAppendFit(const Ts&... vs) {
+  ::folly::detail::reserveInTarget(vs...);
+  toAppend(vs...);
+}
+
+template <class Ts>
+void toAppendFit(const Ts&) {}
+
+/**
+ * Variadic base case: do nothing.
+ */
+template <class Tgt>
+typename std::enable_if<IsSomeString<Tgt>::value>::type
+toAppend(Tgt* result) {
+}
+
+/**
+ * Variadic base case: do nothing.
+ */
+template <class Delimiter, class Tgt>
+typename std::enable_if<IsSomeString<Tgt>::value>::type
+toAppendDelim(const Delimiter& delim, Tgt* result) {
+}
+
+/**
+ * 1 element: same as toAppend.
+ */
+template <class Delimiter, class T, class Tgt>
+typename std::enable_if<IsSomeString<Tgt>::value>::type
+toAppendDelim(const Delimiter& delim, const T& v, Tgt* tgt) {
+  toAppend(v, tgt);
+}
+
+/**
+ * Append to string with a delimiter in between elements. Check out
+ * comments for toAppend for details about memory allocation.
+ */
+template <class Delimiter, class... Ts>
+typename std::enable_if<sizeof...(Ts) >= 3
+  && IsSomeString<
+  typename std::remove_pointer<
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
+toAppendDelim(const Delimiter& delim, const Ts&... vs) {
+  detail::toAppendDelimStrImpl(delim, vs...);
+}
+
+/**
+ * Detail in comment for toAppendFit
+ */
+template <class Delimiter, class... Ts>
+typename std::enable_if<
+  IsSomeString<
+  typename std::remove_pointer<
+    typename detail::last_element<Ts...>::type
+  >::type>::value>::type
+toAppendDelimFit(const Delimiter& delim, const Ts&... vs) {
+  detail::reserveInTargetDelim(delim, vs...);
+  toAppendDelim(delim, vs...);
+}
+
+template <class De, class Ts>
+void toAppendDelimFit(const De&, const Ts&) {}
+
+/**
+ * to<SomeString>(v1, v2, ...) uses toAppend() (see below) as back-end
+ * for all types.
+ */
+template <class Tgt, class... Ts>
+typename std::enable_if<
+  IsSomeString<Tgt>::value && (
+    sizeof...(Ts) != 1 ||
+    !std::is_same<Tgt, typename detail::last_element<Ts...>::type>::value),
+  Tgt>::type
+to(const Ts&... vs) {
+  Tgt result;
+  toAppendFit(vs..., &result);
+  return result;
+}
+
+/**
+ * toDelim<SomeString>(SomeString str) returns itself.
+ */
+template <class Tgt, class Delim, class Src>
+typename std::enable_if<
+  IsSomeString<Tgt>::value && std::is_same<Tgt, Src>::value,
+  Tgt>::type
+toDelim(const Delim& delim, const Src & value) {
+  return value;
+}
+
+/**
+ * toDelim<SomeString>(delim, v1, v2, ...) uses toAppendDelim() as
+ * back-end for all types.
+ */
+template <class Tgt, class Delim, class... Ts>
+typename std::enable_if<
+  IsSomeString<Tgt>::value && (
+    sizeof...(Ts) != 1 ||
+    !std::is_same<Tgt, typename detail::last_element<Ts...>::type>::value),
+  Tgt>::type
+toDelim(const Delim& delim, const Ts&... vs) {
+  Tgt result;
+  toAppendDelimFit(delim, vs..., &result);
+  return result;
+}
+
+/*******************************************************************************
+ * Conversions from string types to integral types.
+ ******************************************************************************/
+
+namespace detail {
+
+/**
+ * Finds the first non-digit in a string. The number of digits
+ * searched depends on the precision of the Tgt integral. Assumes the
+ * string starts with NO whitespace and NO sign.
+ *
+ * The semantics of the routine is:
+ *   for (;; ++b) {
+ *     if (b >= e || !isdigit(*b)) return b;
+ *   }
+ *
+ *  Complete unrolling marks bottom-line (i.e. entire conversion)
+ *  improvements of 20%.
+ */
+  template <class Tgt>
+  const char* findFirstNonDigit(const char* b, const char* e) {
+    for (; b < e; ++b) {
+      auto const c = static_cast<unsigned>(*b) - '0';
+      if (c >= 10) break;
+    }
+    return b;
+  }
+
+  // Maximum value of number when represented as a string
+  template <class T> struct MaxString {
+    static const char*const value;
+  };
+
+  // clang >= 209 now requires "extern" declaration of MaxString template
+#ifdef __clang__
+#if (__clang_major__ * 100 + __clang_minor__) >= 209
+  // see Conv.cpp:29-50
+  extern template struct MaxString<bool>;
+  extern template struct MaxString<uint8_t>;
+  extern template struct MaxString<uint16_t>;
+  extern template struct MaxString<uint32_t>;
+  extern template struct MaxString<unsigned long>;
+  extern template struct MaxString<unsigned long long>;
+#endif
+#endif
+
+
+/*
+ * Lookup tables that converts from a decimal character value to an integral
+ * binary value, shifted by a decimal "shift" multiplier.
+ * For all character values in the range '0'..'9', the table at those
+ * index locations returns the actual decimal value shifted by the multiplier.
+ * For all other values, the lookup table returns an invalid OOR value.
+ */
+// Out-of-range flag value, larger than the largest value that can fit in
+// four decimal bytes (9999), but four of these added up together should
+// still not overflow uint16_t.
+constexpr int32_t OOR = 10000;
+
+FOLLY_ALIGNED(16) constexpr uint16_t shift1[] = {
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 0-9
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  10
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  20
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  30
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,         //  40
+  1, 2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR,
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  60
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  70
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  80
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  90
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 100
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 110
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 120
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 130
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 140
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 150
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 160
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 170
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 180
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 190
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 200
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 210
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 220
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 230
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 240
+  OOR, OOR, OOR, OOR, OOR, OOR                       // 250
+};
+
+FOLLY_ALIGNED(16) constexpr uint16_t shift10[] = {
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 0-9
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  10
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  20
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  30
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,         //  40
+  10, 20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR,
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  60
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  70
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  80
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  90
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 100
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 110
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 120
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 130
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 140
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 150
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 160
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 170
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 180
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 190
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 200
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 210
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 220
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 230
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 240
+  OOR, OOR, OOR, OOR, OOR, OOR                       // 250
+};
+
+FOLLY_ALIGNED(16) constexpr uint16_t shift100[] = {
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 0-9
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  10
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  20
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  30
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,         //  40
+  100, 200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR,
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  60
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  70
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  80
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  90
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 100
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 110
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 120
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 130
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 140
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 150
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 160
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 170
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 180
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 190
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 200
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 210
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 220
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 230
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 240
+  OOR, OOR, OOR, OOR, OOR, OOR                       // 250
+};
+
+FOLLY_ALIGNED(16) constexpr uint16_t shift1000[] = {
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 0-9
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  10
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  20
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  30
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,         //  40
+  1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR,
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  60
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  70
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  80
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  //  90
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 100
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 110
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 120
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 130
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 140
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 150
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 160
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 170
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 180
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 190
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 200
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 210
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 220
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 230
+  OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR,  // 240
+  OOR, OOR, OOR, OOR, OOR, OOR                       // 250
+};
+
+/**
+ * String represented as a pair of pointers to char to unsigned
+ * integrals. Assumes NO whitespace before or after, and also that the
+ * string is composed entirely of digits. Tgt must be unsigned, and no
+ * sign is allowed in the string (even it's '+'). String may be empty,
+ * in which case digits_to throws.
+ */
+  template <class Tgt>
+  Tgt digits_to(const char * b, const char * e) {
+
+    static_assert(!std::is_signed<Tgt>::value, "Unsigned type expected");
+    assert(b <= e);
+
+    const size_t size = e - b;
+
+    /* Although the string is entirely made of digits, we still need to
+     * check for overflow.
+     */
+    if (size >= std::numeric_limits<Tgt>::digits10 + 1) {
+      // Leading zeros? If so, recurse to keep things simple
+      if (b < e && *b == '0') {
+        for (++b;; ++b) {
+          if (b == e) return 0; // just zeros, e.g. "0000"
+          if (*b != '0') return digits_to<Tgt>(b, e);
+        }
+      }
+      FOLLY_RANGE_CHECK_BEGIN_END(
+        size == std::numeric_limits<Tgt>::digits10 + 1 &&
+        strncmp(b, detail::MaxString<Tgt>::value, size) <= 0,
+        "Numeric overflow upon conversion", b, e);
+    }
+
+    // Here we know that the number won't overflow when
+    // converted. Proceed without checks.
+
+    Tgt result = 0;
+
+    for (; e - b >= 4; b += 4) {
+      result *= 10000;
+      const int32_t r0 = shift1000[static_cast<size_t>(b[0])];
+      const int32_t r1 = shift100[static_cast<size_t>(b[1])];
+      const int32_t r2 = shift10[static_cast<size_t>(b[2])];
+      const int32_t r3 = shift1[static_cast<size_t>(b[3])];
+      const auto sum = r0 + r1 + r2 + r3;
+      assert(sum < OOR && "Assumption: string only has digits");
+      result += sum;
+    }
+
+    switch (e - b) {
+      case 3: {
+        const int32_t r0 = shift100[static_cast<size_t>(b[0])];
+        const int32_t r1 = shift10[static_cast<size_t>(b[1])];
+        const int32_t r2 = shift1[static_cast<size_t>(b[2])];
+        const auto sum = r0 + r1 + r2;
+        assert(sum < OOR && "Assumption: string only has digits");
+        return result * 1000 + sum;
+      }
+      case 2: {
+        const int32_t r0 = shift10[static_cast<size_t>(b[0])];
+        const int32_t r1 = shift1[static_cast<size_t>(b[1])];
+        const auto sum = r0 + r1;
+        assert(sum < OOR && "Assumption: string only has digits");
+        return result * 100 + sum;
+      }
+      case 1: {
+        const int32_t sum = shift1[static_cast<size_t>(b[0])];
+        assert(sum < OOR && "Assumption: string only has digits");
+        return result * 10 + sum;
+      }
+    }
+
+    assert(b == e);
+    FOLLY_RANGE_CHECK_BEGIN_END(size > 0,
+                                "Found no digits to convert in input", b, e);
+    return result;
+  }
+
+
+  bool str_to_bool(StringPiece * src);
+
+}                                 // namespace detail
+
+/**
+ * String represented as a pair of pointers to char to unsigned
+ * integrals. Assumes NO whitespace before or after.
+ */
+template <class Tgt>
+typename std::enable_if<
+  std::is_integral<Tgt>::value && !std::is_signed<Tgt>::value
+  && !std::is_same<typename std::remove_cv<Tgt>::type, bool>::value,
+  Tgt>::type
+to(const char * b, const char * e) {
+  return detail::digits_to<Tgt>(b, e);
+}
+
+/**
+ * String represented as a pair of pointers to char to signed
+ * integrals. Assumes NO whitespace before or after. Allows an
+ * optional leading sign.
+ */
+template <class Tgt>
+typename std::enable_if<
+  std::is_integral<Tgt>::value && std::is_signed<Tgt>::value,
+  Tgt>::type
+to(const char * b, const char * e) {
+  FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral",
+                    to<std::string>("b: ", intptr_t(b), " e: ", intptr_t(e)));
+  if (!isdigit(*b)) {
+    if (*b == '-') {
+      Tgt result = -to<typename std::make_unsigned<Tgt>::type>(b + 1, e);
+      FOLLY_RANGE_CHECK_BEGIN_END(result <= 0, "Negative overflow.", b, e);
+      return result;
+    }
+    FOLLY_RANGE_CHECK_BEGIN_END(*b == '+', "Invalid lead character", b, e);
+    ++b;
+  }
+  Tgt result = to<typename std::make_unsigned<Tgt>::type>(b, e);
+  FOLLY_RANGE_CHECK_BEGIN_END(result >= 0, "Overflow", b, e);
+  return result;
+}
+
+/**
+ * Parsing strings to integrals. These routines differ from
+ * to<integral>(string) in that they take a POINTER TO a StringPiece
+ * and alter that StringPiece to reflect progress information.
+ */
+
+/**
+ * StringPiece to integrals, with progress information. Alters the
+ * StringPiece parameter to munch the already-parsed characters.
+ */
+template <class Tgt>
+typename std::enable_if<
+  std::is_integral<Tgt>::value
+  && !std::is_same<typename std::remove_cv<Tgt>::type, bool>::value,
+  Tgt>::type
+to(StringPiece * src) {
+
+  auto b = src->data(), past = src->data() + src->size();
+  for (;; ++b) {
+    FOLLY_RANGE_CHECK_STRINGPIECE(b < past,
+                                  "No digits found in input string", *src);
+    if (!isspace(*b)) break;
+  }
+
+  auto m = b;
+
+  // First digit is customized because we test for sign
+  bool negative = false;
+  /* static */ if (std::is_signed<Tgt>::value) {
+    if (!isdigit(*m)) {
+      if (*m == '-') {
+        negative = true;
+      } else {
+        FOLLY_RANGE_CHECK_STRINGPIECE(*m == '+', "Invalid leading character in "
+                                      "conversion to integral", *src);
+      }
+      ++b;
+      ++m;
+    }
+  }
+  FOLLY_RANGE_CHECK_STRINGPIECE(m < past, "No digits found in input string",
+                                *src);
+  FOLLY_RANGE_CHECK_STRINGPIECE(isdigit(*m), "Non-digit character found", *src);
+  m = detail::findFirstNonDigit<Tgt>(m + 1, past);
+
+  Tgt result;
+  /* static */ if (!std::is_signed<Tgt>::value) {
+    result = detail::digits_to<typename std::make_unsigned<Tgt>::type>(b, m);
+  } else {
+    auto t = detail::digits_to<typename std::make_unsigned<Tgt>::type>(b, m);
+    if (negative) {
+      result = -t;
+      FOLLY_RANGE_CHECK_STRINGPIECE(is_non_positive(result),
+                                    "Negative overflow", *src);
+    } else {
+      result = t;
+      FOLLY_RANGE_CHECK_STRINGPIECE(is_non_negative(result), "Overflow", *src);
+    }
+  }
+  src->advance(m - src->data());
+  return result;
+}
+
+/**
+ * StringPiece to bool, with progress information. Alters the
+ * StringPiece parameter to munch the already-parsed characters.
+ */
+template <class Tgt>
+typename std::enable_if<
+  std::is_same<typename std::remove_cv<Tgt>::type, bool>::value,
+  Tgt>::type
+to(StringPiece * src) {
+  return detail::str_to_bool(src);
+}
+
+namespace detail {
+
+/**
+ * Enforce that the suffix following a number is made up only of whitespace.
+ */
+inline void enforceWhitespace(const char* b, const char* e) {
+  for (; b != e; ++b) {
+    FOLLY_RANGE_CHECK_BEGIN_END(isspace(*b),
+                                to<std::string>("Non-whitespace: ", *b),
+                                b, e);
+  }
+}
+
+}  // namespace detail
+
+/**
+ * String or StringPiece to integrals. Accepts leading and trailing
+ * whitespace, but no non-space trailing characters.
+ */
+template <class Tgt>
+typename std::enable_if<
+  std::is_integral<Tgt>::value,
+  Tgt>::type
+to(StringPiece src) {
+  Tgt result = to<Tgt>(&src);
+  detail::enforceWhitespace(src.data(), src.data() + src.size());
+  return result;
+}
+
+/*******************************************************************************
+ * Conversions from string types to floating-point types.
+ ******************************************************************************/
+
+/**
+ * StringPiece to double, with progress information. Alters the
+ * StringPiece parameter to munch the already-parsed characters.
+ */
+template <class Tgt>
+inline typename std::enable_if<
+  std::is_floating_point<Tgt>::value,
+  Tgt>::type
+to(StringPiece *const src) {
+  using namespace double_conversion;
+  static StringToDoubleConverter
+    conv(StringToDoubleConverter::ALLOW_TRAILING_JUNK
+         | StringToDoubleConverter::ALLOW_LEADING_SPACES,
+         0.0,
+         // return this for junk input string
+         std::numeric_limits<double>::quiet_NaN(),
+         nullptr, nullptr);
+
+  FOLLY_RANGE_CHECK_STRINGPIECE(!src->empty(),
+                                "No digits found in input string", *src);
+
+  int length;
+  auto result = conv.StringToDouble(src->data(),
+                                    static_cast<int>(src->size()),
+                                    &length); // processed char count
+
+  if (!std::isnan(result)) {
+    src->advance(length);
+    return result;
+  }
+
+  for (;; src->advance(1)) {
+    if (src->empty()) {
+      throw std::range_error("Unable to convert an empty string"
+                             " to a floating point value.");
+    }
+    if (!isspace(src->front())) {
+      break;
+    }
+  }
+
+  // Was that "inf[inity]"?
+  if (src->size() >= 3 && toupper((*src)[0]) == 'I'
+        && toupper((*src)[1]) == 'N' && toupper((*src)[2]) == 'F') {
+    if (src->size() >= 8 &&
+        toupper((*src)[3]) == 'I' &&
+        toupper((*src)[4]) == 'N' &&
+        toupper((*src)[5]) == 'I' &&
+        toupper((*src)[6]) == 'T' &&
+        toupper((*src)[7]) == 'Y') {
+      src->advance(8);
+    } else {
+      src->advance(3);
+    }
+    return std::numeric_limits<Tgt>::infinity();
+  }
+
+  // Was that "-inf[inity]"?
+  if (src->size() >= 4 && toupper((*src)[0]) == '-'
+      && toupper((*src)[1]) == 'I' && toupper((*src)[2]) == 'N'
+      && toupper((*src)[3]) == 'F') {
+    if (src->size() >= 9 &&
+        toupper((*src)[4]) == 'I' &&
+        toupper((*src)[5]) == 'N' &&
+        toupper((*src)[6]) == 'I' &&
+        toupper((*src)[7]) == 'T' &&
+        toupper((*src)[8]) == 'Y') {
+      src->advance(9);
+    } else {
+      src->advance(4);
+    }
+    return -std::numeric_limits<Tgt>::infinity();
+  }
+
+  // "nan"?
+  if (src->size() >= 3 && toupper((*src)[0]) == 'N'
+        && toupper((*src)[1]) == 'A' && toupper((*src)[2]) == 'N') {
+    src->advance(3);
+    return std::numeric_limits<Tgt>::quiet_NaN();
+  }
+
+  // "-nan"?
+  if (src->size() >= 4 &&
+      toupper((*src)[0]) == '-' &&
+      toupper((*src)[1]) == 'N' &&
+      toupper((*src)[2]) == 'A' &&
+      toupper((*src)[3]) == 'N') {
+    src->advance(4);
+    return -std::numeric_limits<Tgt>::quiet_NaN();
+  }
+
+  // All bets are off
+  throw std::range_error("Unable to convert \"" + src->toString()
+                         + "\" to a floating point value.");
+}
+
+/**
+ * Any string, const char*, or StringPiece to double.
+ */
+template <class Tgt>
+typename std::enable_if<
+  std::is_floating_point<Tgt>::value,
+  Tgt>::type
+to(StringPiece src) {
+  Tgt result = to<double>(&src);
+  detail::enforceWhitespace(src.data(), src.data() + src.size());
+  return result;
+}
+
+/*******************************************************************************
+ * Integral to floating point and back
+ ******************************************************************************/
+
+/**
+ * Checked conversion from integral to flating point and back. The
+ * result must be convertible back to the source type without loss of
+ * precision. This seems Draconian but sometimes is what's needed, and
+ * complements existing routines nicely. For various rounding
+ * routines, see <math>.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  (std::is_integral<Src>::value && std::is_floating_point<Tgt>::value)
+  ||
+  (std::is_floating_point<Src>::value && std::is_integral<Tgt>::value),
+  Tgt>::type
+to(const Src & value) {
+  Tgt result = value;
+  auto witness = static_cast<Src>(result);
+  if (value != witness) {
+    throw std::range_error(
+      to<std::string>("to<>: loss of precision when converting ", value,
+#ifdef FOLLY_HAS_RTTI
+                      " to type ", typeid(Tgt).name()
+#else
+                      " to other type"
+#endif
+                      ).c_str());
+  }
+  return result;
+}
+
+/*******************************************************************************
+ * Enum to anything and back
+ ******************************************************************************/
+
+#if defined(__clang__) || __GNUC_PREREQ(4, 7)
+// std::underlying_type became available by gcc 4.7.0
+
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value, Tgt>::type
+to(const Src & value) {
+  return to<Tgt>(static_cast<typename std::underlying_type<Src>::type>(value));
+}
+
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Tgt>::value && !std::is_same<Src, Tgt>::value, Tgt>::type
+to(const Src & value) {
+  return static_cast<Tgt>(to<typename std::underlying_type<Tgt>::type>(value));
+}
+
+#else
+
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value, Tgt>::type
+to(const Src & value) {
+  /* static */ if (Src(-1) < 0) {
+    /* static */ if (sizeof(Src) <= sizeof(int)) {
+      return to<Tgt>(static_cast<int>(value));
+    } else {
+      return to<Tgt>(static_cast<long>(value));
+    }
+  } else {
+    /* static */ if (sizeof(Src) <= sizeof(int)) {
+      return to<Tgt>(static_cast<unsigned int>(value));
+    } else {
+      return to<Tgt>(static_cast<unsigned long>(value));
+    }
+  }
+}
+
+template <class Tgt, class Src>
+typename std::enable_if<
+  std::is_enum<Tgt>::value && !std::is_same<Src, Tgt>::value, Tgt>::type
+to(const Src & value) {
+  /* static */ if (Tgt(-1) < 0) {
+    /* static */ if (sizeof(Tgt) <= sizeof(int)) {
+      return static_cast<Tgt>(to<int>(value));
+    } else {
+      return static_cast<Tgt>(to<long>(value));
+    }
+  } else {
+    /* static */ if (sizeof(Tgt) <= sizeof(int)) {
+      return static_cast<Tgt>(to<unsigned int>(value));
+    } else {
+      return static_cast<Tgt>(to<unsigned long>(value));
+    }
+  }
+}
+
+#endif // gcc 4.7 onwards
+
+} // namespace folly
+
+// FOLLY_CONV_INTERNAL is defined by Conv.cpp.  Keep the FOLLY_RANGE_CHECK
+// macro for use in Conv.cpp, but #undefine it everywhere else we are included,
+// to avoid defining this global macro name in other files that include Conv.h.
+#ifndef FOLLY_CONV_INTERNAL
+#undef FOLLY_RANGE_CHECK
+#undef FOLLY_RANGE_CHECK_BEGIN_END
+#undef FOLLY_RANGE_CHECK_STRINGPIECE
+#undef FOLLY_RANGE_CHECK_STRINGIZE
+#undef FOLLY_RANGE_CHECK_STRINGIZE2
+#endif
+
+#endif /* FOLLY_BASE_CONV_H_ */
diff --git a/faux-folly/folly/Demangle.cpp b/faux-folly/folly/Demangle.cpp
new file mode 100644
index 0000000..ee58726
--- /dev/null
+++ b/faux-folly/folly/Demangle.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2015 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/Demangle.h>
+
+#include <algorithm>
+#include <string.h>
+
+#include <folly/Malloc.h>
+
+#if FOLLY_HAVE_CPLUS_DEMANGLE_V3_CALLBACK
+# include <cxxabi.h>
+
+// From libiberty
+//
+// TODO(tudorb): Detect this with autoconf for the open-source version.
+//
+// __attribute__((__weak__)) doesn't work, because cplus_demangle_v3_callback
+// is exported by an object file in libiberty.a, and the ELF spec says
+// "The link editor does not extract archive members to resolve undefined weak
+// symbols" (but, interestingly enough, will resolve undefined weak symbols
+// with definitions from archive members that were extracted in order to
+// resolve an undefined global (strong) symbol)
+
+# ifndef DMGL_NO_OPTS
+#  define FOLLY_DEFINED_DMGL 1
+#  define DMGL_NO_OPTS    0          /* For readability... */
+#  define DMGL_PARAMS     (1 << 0)   /* Include function args */
+#  define DMGL_ANSI       (1 << 1)   /* Include const, volatile, etc */
+#  define DMGL_JAVA       (1 << 2)   /* Demangle as Java rather than C++. */
+#  define DMGL_VERBOSE    (1 << 3)   /* Include implementation details.  */
+#  define DMGL_TYPES      (1 << 4)   /* Also try to demangle type encodings.  */
+#  define DMGL_RET_POSTFIX (1 << 5)  /* Print function return types (when
+                                        present) after function signature */
+# endif
+
+extern "C" int cplus_demangle_v3_callback(
+    const char* mangled,
+    int options,  // We use DMGL_PARAMS | DMGL_TYPES, aka 0x11
+    void (*callback)(const char*, size_t, void*),
+    void* arg);
+
+#endif
+
+namespace {
+
+// glibc doesn't have strlcpy
+size_t my_strlcpy(char* dest, const char* src, size_t size) {
+  size_t len = strlen(src);
+  if (size != 0) {
+    size_t n = std::min(len, size - 1);  // always null terminate!
+    memcpy(dest, src, n);
+    dest[n] = '\0';
+  }
+  return len;
+}
+
+}  // namespace
+
+namespace folly {
+
+#if FOLLY_HAVE_CPLUS_DEMANGLE_V3_CALLBACK
+
+fbstring demangle(const char* name) {
+  int status;
+  size_t len = 0;
+  // malloc() memory for the demangled type name
+  char* demangled = abi::__cxa_demangle(name, nullptr, &len, &status);
+  if (status != 0) {
+    return name;
+  }
+  // len is the length of the buffer (including NUL terminator and maybe
+  // other junk)
+  return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString());
+}
+
+namespace {
+
+struct DemangleBuf {
+  char* dest;
+  size_t remaining;
+  size_t total;
+};
+
+void demangleCallback(const char* str, size_t size, void* p) {
+  DemangleBuf* buf = static_cast<DemangleBuf*>(p);
+  size_t n = std::min(buf->remaining, size);
+  memcpy(buf->dest, str, n);
+  buf->dest += n;
+  buf->remaining -= n;
+  buf->total += size;
+}
+
+}  // namespace
+
+size_t demangle(const char* name, char* out, size_t outSize) {
+  DemangleBuf dbuf;
+  dbuf.dest = out;
+  dbuf.remaining = outSize ? outSize - 1 : 0;   // leave room for null term
+  dbuf.total = 0;
+
+  // Unlike most library functions, this returns 1 on success and 0 on failure
+  int status = cplus_demangle_v3_callback(
+      name,
+      DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES,
+      demangleCallback,
+      &dbuf);
+  if (status == 0) {  // failed, return original
+    return my_strlcpy(out, name, outSize);
+  }
+  if (outSize != 0) {
+    *dbuf.dest = '\0';
+  }
+  return dbuf.total;
+}
+
+#else
+
+fbstring demangle(const char* name) {
+  return name;
+}
+
+size_t demangle(const char* name, char* out, size_t outSize) {
+  return my_strlcpy(out, name, outSize);
+}
+
+#endif
+
+} // folly
diff --git a/faux-folly/folly/Demangle.h b/faux-folly/folly/Demangle.h
new file mode 100644
index 0000000..1422e81
--- /dev/null
+++ b/faux-folly/folly/Demangle.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 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/FBString.h>
+
+namespace folly {
+
+/**
+ * Return the demangled (prettyfied) version of a C++ type.
+ *
+ * This function tries to produce a human-readable type, but the type name will
+ * be returned unchanged in case of error or if demangling isn't supported on
+ * your system.
+ *
+ * Use for debugging -- do not rely on demangle() returning anything useful.
+ *
+ * This function may allocate memory (and therefore throw std::bad_alloc).
+ */
+fbstring demangle(const char* name);
+inline fbstring demangle(const std::type_info& type) {
+  return demangle(type.name());
+}
+
+/**
+ * Return the demangled (prettyfied) version of a C++ type in a user-provided
+ * buffer.
+ *
+ * The semantics are the same as for snprintf or strlcpy: bufSize is the size
+ * of the buffer, the string is always null-terminated, and the return value is
+ * the number of characters (not including the null terminator) that would have
+ * been written if the buffer was big enough. (So a return value >= bufSize
+ * indicates that the output was truncated)
+ *
+ * This function does not allocate memory and is async-signal-safe.
+ *
+ * Note that the underlying function for the fbstring-returning demangle is
+ * somewhat standard (abi::__cxa_demangle, which uses malloc), the underlying
+ * function for this version is less so (cplus_demangle_v3_callback from
+ * libiberty), so it is possible for the fbstring version to work, while this
+ * version returns the original, mangled name.
+ */
+size_t demangle(const char* name, char* buf, size_t bufSize);
+inline size_t demangle(const std::type_info& type, char* buf, size_t bufSize) {
+  return demangle(type.name(), buf, bufSize);
+}
+
+}
diff --git a/faux-folly/folly/DynamicConverter.h b/faux-folly/folly/DynamicConverter.h
new file mode 100644
index 0000000..4c8b910
--- /dev/null
+++ b/faux-folly/folly/DynamicConverter.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Nicholas Ormrod <njormrod@fb.com>
+
+#ifndef DYNAMIC_CONVERTER_H
+#define DYNAMIC_CONVERTER_H
+
+#include <folly/dynamic.h>
+namespace folly {
+  template <typename T> T convertTo(const dynamic&);
+  template <typename T> dynamic toDynamic(const T&);
+}
+
+/**
+ * convertTo returns a well-typed representation of the input dynamic.
+ *
+ * Example:
+ *
+ *   dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
+ *   auto vvi = convertTo<fbvector<fbvector<int>>>(d);
+ *
+ * See docs/DynamicConverter.md for supported types and customization
+ */
+
+
+#include <type_traits>
+#include <iterator>
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <boost/mpl/has_xxx.hpp>
+#include <folly/Likely.h>
+
+namespace folly {
+
+///////////////////////////////////////////////////////////////////////////////
+// traits
+
+namespace dynamicconverter_detail {
+
+BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
+BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
+BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
+
+template <typename T> struct iterator_class_is_container {
+  typedef std::reverse_iterator<typename T::iterator> some_iterator;
+  enum { value = has_value_type<T>::value &&
+              std::is_constructible<T, some_iterator, some_iterator>::value };
+};
+
+template <typename T>
+using class_is_container = typename
+  std::conditional<
+    has_iterator<T>::value,
+    iterator_class_is_container<T>,
+    std::false_type
+  >::type;
+
+template <typename T> struct class_is_range {
+  enum { value = has_value_type<T>::value &&
+                 has_iterator<T>::value };
+};
+
+
+template <typename T> struct is_container
+  : std::conditional<
+      std::is_class<T>::value,
+      class_is_container<T>,
+      std::false_type
+    >::type {};
+
+template <typename T> struct is_range
+  : std::conditional<
+      std::is_class<T>::value,
+      class_is_range<T>,
+      std::false_type
+    >::type {};
+
+template <typename T> struct is_map
+  : std::integral_constant<
+      bool,
+      is_range<T>::value && has_mapped_type<T>::value
+    > {};
+
+} // namespace dynamicconverter_detail
+
+///////////////////////////////////////////////////////////////////////////////
+// custom iterators
+
+/**
+ * We have iterators that dereference to dynamics, but need iterators
+ * that dereference to typename T.
+ *
+ * Implementation details:
+ *   1. We cache the value of the dereference operator. This is necessary
+ *      because boost::iterator_adaptor requires *it to return a
+ *      reference.
+ *   2. For const reasons, we cannot call operator= to refresh the
+ *      cache: we must call the destructor then placement new.
+ */
+
+namespace dynamicconverter_detail {
+
+template<typename T>
+struct Dereferencer {
+  static inline void
+  derefToCache(T* mem, const dynamic::const_item_iterator& it) {
+    throw TypeError("array", dynamic::Type::OBJECT);
+  }
+
+  static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
+    new (mem) T(convertTo<T>(*it));
+  }
+};
+
+template<typename F, typename S>
+struct Dereferencer<std::pair<F, S>> {
+  static inline void
+  derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
+    new (mem) std::pair<F, S>(
+        convertTo<F>(it->first), convertTo<S>(it->second)
+    );
+  }
+
+  // Intentional duplication of the code in Dereferencer
+  template <typename T>
+  static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
+    new (mem) T(convertTo<T>(*it));
+  }
+};
+
+template <typename T, typename It>
+class Transformer : public boost::iterator_adaptor<
+                             Transformer<T, It>,
+                             It,
+                             typename T::value_type
+                           > {
+  friend class boost::iterator_core_access;
+
+  typedef typename T::value_type ttype;
+
+  mutable ttype cache_;
+  mutable bool valid_;
+
+  void increment() {
+    ++this->base_reference();
+    valid_ = false;
+  }
+
+  ttype& dereference() const {
+    if (LIKELY(!valid_)) {
+      cache_.~ttype();
+      Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
+      valid_ = true;
+    }
+    return cache_;
+  }
+
+public:
+  explicit Transformer(const It& it)
+    : Transformer::iterator_adaptor_(it), valid_(false) {}
+};
+
+// conversion factory
+template <typename T, typename It>
+inline std::move_iterator<Transformer<T, It>>
+conversionIterator(const It& it) {
+  return std::make_move_iterator(Transformer<T, It>(it));
+}
+
+} // namespace dynamicconverter_detail
+
+///////////////////////////////////////////////////////////////////////////////
+// DynamicConverter specializations
+
+/**
+ * Each specialization of DynamicConverter has the function
+ *     'static T convert(const dynamic&);'
+ */
+
+// default - intentionally unimplemented
+template <typename T, typename Enable = void> struct DynamicConverter;
+
+// boolean
+template <>
+struct DynamicConverter<bool> {
+  static bool convert(const dynamic& d) {
+    return d.asBool();
+  }
+};
+
+// integrals
+template <typename T>
+struct DynamicConverter<T,
+    typename std::enable_if<std::is_integral<T>::value &&
+                            !std::is_same<T, bool>::value>::type> {
+  static T convert(const dynamic& d) {
+    return folly::to<T>(d.asInt());
+  }
+};
+
+// floating point
+template <typename T>
+struct DynamicConverter<T,
+    typename std::enable_if<std::is_floating_point<T>::value>::type> {
+  static T convert(const dynamic& d) {
+    return folly::to<T>(d.asDouble());
+  }
+};
+
+// fbstring
+template <>
+struct DynamicConverter<folly::fbstring> {
+  static folly::fbstring convert(const dynamic& d) {
+    return d.asString();
+  }
+};
+
+// std::string
+template <>
+struct DynamicConverter<std::string> {
+  static std::string convert(const dynamic& d) {
+    return d.asString().toStdString();
+  }
+};
+
+// std::pair
+template <typename F, typename S>
+struct DynamicConverter<std::pair<F,S>> {
+  static std::pair<F, S> convert(const dynamic& d) {
+    if (d.isArray() && d.size() == 2) {
+      return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
+    } else if (d.isObject() && d.size() == 1) {
+      auto it = d.items().begin();
+      return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
+    } else {
+      throw TypeError("array (size 2) or object (size 1)", d.type());
+    }
+  }
+};
+
+// containers
+template <typename C>
+struct DynamicConverter<C,
+    typename std::enable_if<
+      dynamicconverter_detail::is_container<C>::value>::type> {
+  static C convert(const dynamic& d) {
+    if (d.isArray()) {
+      return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
+               dynamicconverter_detail::conversionIterator<C>(d.end()));
+    } else if (d.isObject()) {
+      return C(dynamicconverter_detail::conversionIterator<C>
+                 (d.items().begin()),
+               dynamicconverter_detail::conversionIterator<C>
+                 (d.items().end()));
+    } else {
+      throw TypeError("object or array", d.type());
+    }
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// DynamicConstructor specializations
+
+/**
+ * Each specialization of DynamicConstructor has the function
+ *     'static dynamic construct(const C&);'
+ */
+
+// default
+template <typename C, typename Enable = void>
+struct DynamicConstructor {
+  static dynamic construct(const C& x) {
+    return dynamic(x);
+  }
+};
+
+// maps
+template<typename C>
+struct DynamicConstructor<C,
+    typename std::enable_if<
+      dynamicconverter_detail::is_map<C>::value>::type> {
+  static dynamic construct(const C& x) {
+    dynamic d = dynamic::object;
+    for (auto& pair : x) {
+      d.insert(toDynamic(pair.first), toDynamic(pair.second));
+    }
+    return d;
+  }
+};
+
+// other ranges
+template<typename C>
+struct DynamicConstructor<C,
+    typename std::enable_if<
+      !dynamicconverter_detail::is_map<C>::value &&
+      !std::is_constructible<StringPiece, const C&>::value &&
+      dynamicconverter_detail::is_range<C>::value>::type> {
+  static dynamic construct(const C& x) {
+    dynamic d = {};
+    for (auto& item : x) {
+      d.push_back(toDynamic(item));
+    }
+    return d;
+  }
+};
+
+// pair
+template<typename A, typename B>
+struct DynamicConstructor<std::pair<A, B>, void> {
+  static dynamic construct(const std::pair<A, B>& x) {
+    dynamic d = {};
+    d.push_back(toDynamic(x.first));
+    d.push_back(toDynamic(x.second));
+    return d;
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// implementation
+
+template <typename T>
+T convertTo(const dynamic& d) {
+  return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
+}
+
+template<typename T>
+dynamic toDynamic(const T& x) {
+  return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
+}
+
+} // namespace folly
+
+#endif // DYNAMIC_CONVERTER_H
diff --git a/faux-folly/folly/Exception.h b/faux-folly/folly/Exception.h
new file mode 100644
index 0000000..28188c6
--- /dev/null
+++ b/faux-folly/folly/Exception.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_EXCEPTION_H_
+#define FOLLY_EXCEPTION_H_
+
+#include <errno.h>
+
+#include <cstdio>
+#include <stdexcept>
+#include <system_error>
+
+#include <folly/Conv.h>
+#include <folly/FBString.h>
+#include <folly/Likely.h>
+#include <folly/Portability.h>
+
+namespace folly {
+
+// Various helpers to throw appropriate std::system_error exceptions from C
+// library errors (returned in errno, as positive return values (many POSIX
+// functions), or as negative return values (Linux syscalls))
+//
+// The *Explicit functions take an explicit value for errno.
+
+// Helper to throw std::system_error
+FOLLY_NORETURN void throwSystemErrorExplicit(int err, const char*);
+inline void throwSystemErrorExplicit(int err, const char* msg) {
+  throw std::system_error(err, std::system_category(), msg);
+}
+
+template <class... Args>
+FOLLY_NORETURN void throwSystemErrorExplicit(int, Args&&... args);
+template <class... Args>
+void throwSystemErrorExplicit(int err, Args&&... args) {
+  throwSystemErrorExplicit(
+      err, to<fbstring>(std::forward<Args>(args)...).c_str());
+}
+
+// Helper to throw std::system_error from errno and components of a string
+template <class... Args>
+FOLLY_NORETURN void throwSystemError(Args&&... args);
+template <class... Args>
+void throwSystemError(Args&&... args) {
+  throwSystemErrorExplicit(errno, std::forward<Args>(args)...);
+}
+
+// Check a Posix return code (0 on success, error number on error), throw
+// on error.
+template <class... Args>
+void checkPosixError(int err, Args&&... args) {
+  if (UNLIKELY(err != 0)) {
+    throwSystemErrorExplicit(err, std::forward<Args>(args)...);
+  }
+}
+
+// Check a Linux kernel-style return code (>= 0 on success, negative error
+// number on error), throw on error.
+template <class... Args>
+void checkKernelError(ssize_t ret, Args&&... args) {
+  if (UNLIKELY(ret < 0)) {
+    throwSystemErrorExplicit(-ret, std::forward<Args>(args)...);
+  }
+}
+
+// Check a traditional Unix return code (-1 and sets errno on error), throw
+// on error.
+template <class... Args>
+void checkUnixError(ssize_t ret, Args&&... args) {
+  if (UNLIKELY(ret == -1)) {
+    throwSystemError(std::forward<Args>(args)...);
+  }
+}
+
+template <class... Args>
+void checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) {
+  if (UNLIKELY(ret == -1)) {
+    throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);
+  }
+}
+
+// Check the return code from a fopen-style function (returns a non-nullptr
+// FILE* on success, nullptr on error, sets errno).  Works with fopen, fdopen,
+// freopen, tmpfile, etc.
+template <class... Args>
+void checkFopenError(FILE* fp, Args&&... args) {
+  if (UNLIKELY(!fp)) {
+    throwSystemError(std::forward<Args>(args)...);
+  }
+}
+
+template <class... Args>
+void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) {
+  if (UNLIKELY(!fp)) {
+    throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);
+  }
+}
+
+template <typename E, typename V, typename... Args>
+void throwOnFail(V&& value, Args&&... args) {
+  if (!value) {
+    throw E(std::forward<Args>(args)...);
+  }
+}
+
+/**
+ * If cond is not true, raise an exception of type E.  E must have a ctor that
+ * works with const char* (a description of the failure).
+ */
+#define CHECK_THROW(cond, E) \
+  ::folly::throwOnFail<E>((cond), "Check failed: " #cond)
+
+}  // namespace folly
+
+#endif /* FOLLY_EXCEPTION_H_ */
diff --git a/faux-folly/folly/ExceptionWrapper.h b/faux-folly/folly/ExceptionWrapper.h
new file mode 100644
index 0000000..98ff9cb
--- /dev/null
+++ b/faux-folly/folly/ExceptionWrapper.h
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_EXCEPTIONWRAPPER_H
+#define FOLLY_EXCEPTIONWRAPPER_H
+
+#include <cassert>
+#include <exception>
+#include <memory>
+#include <folly/String.h>
+#include <folly/detail/ExceptionWrapper.h>
+
+namespace folly {
+
+/*
+ * Throwing exceptions can be a convenient way to handle errors. Storing
+ * exceptions in an exception_ptr makes it easy to handle exceptions in a
+ * different thread or at a later time. exception_ptr can also be used in a very
+ * generic result/exception wrapper.
+ *
+ * However, there are some issues with throwing exceptions and
+ * std::exception_ptr. These issues revolve around throw being expensive,
+ * particularly in a multithreaded environment (see
+ * ExceptionWrapperBenchmark.cpp).
+ *
+ * Imagine we have a library that has an API which returns a result/exception
+ * wrapper. Let's consider some approaches for implementing this wrapper.
+ * First, we could store a std::exception. This approach loses the derived
+ * exception type, which can make exception handling more difficult for users
+ * that prefer rethrowing the exception. We could use a folly::dynamic for every
+ * possible type of exception. This is not very flexible - adding new types of
+ * exceptions requires a change to the result/exception wrapper. We could use an
+ * exception_ptr. However, constructing an exception_ptr as well as accessing
+ * the error requires a call to throw. That means that there will be two calls
+ * to throw in order to process the exception. For performance sensitive
+ * applications, this may be unacceptable.
+ *
+ * exception_wrapper is designed to handle exception management for both
+ * convenience and high performance use cases. make_exception_wrapper is
+ * templated on derived type, allowing us to rethrow the exception properly for
+ * users that prefer convenience. These explicitly named exception types can
+ * therefore be handled without any peformance penalty.  exception_wrapper is
+ * also flexible enough to accept any type. If a caught exception is not of an
+ * explicitly named type, then std::exception_ptr is used to preserve the
+ * exception state. For performance sensitive applications, the accessor methods
+ * can test or extract a pointer to a specific exception type with very little
+ * overhead.
+ *
+ * Example usage:
+ *
+ * exception_wrapper globalExceptionWrapper;
+ *
+ * // Thread1
+ * void doSomethingCrazy() {
+ *   int rc = doSomethingCrazyWithLameReturnCodes();
+ *   if (rc == NAILED_IT) {
+ *     globalExceptionWrapper = exception_wrapper();
+ *   } else if (rc == FACE_PLANT) {
+ *     globalExceptionWrapper = make_exception_wrapper<FacePlantException>();
+ *   } else if (rc == FAIL_WHALE) {
+ *     globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
+ *   }
+ * }
+ *
+ * // Thread2: Exceptions are ok!
+ * void processResult() {
+ *   try {
+ *     globalExceptionWrapper.throwException();
+ *   } catch (const FacePlantException& e) {
+ *     LOG(ERROR) << "FACEPLANT!";
+ *   } catch (const FailWhaleException& e) {
+ *     LOG(ERROR) << "FAILWHALE!";
+ *   }
+ * }
+ *
+ * // Thread2: Exceptions are bad!
+ * void processResult() {
+ *   auto ep = globalExceptionWrapper.get();
+ *   if (!ep.with_exception<FacePlantException>([&](
+ *     FacePlantException& faceplant) {
+ *       LOG(ERROR) << "FACEPLANT";
+ *     })) {
+ *     ep.with_exception<FailWhaleException>([&](
+ *       FailWhaleException& failwhale) {
+ *         LOG(ERROR) << "FAILWHALE!";
+ *       });
+ *   }
+ * }
+ *
+ */
+class exception_wrapper {
+ protected:
+  template <typename Ex>
+  struct optimize;
+
+ public:
+  exception_wrapper() = default;
+
+  // Implicitly construct an exception_wrapper from a qualifying exception.
+  // See the optimize struct for details.
+  template <typename Ex, typename =
+    typename std::enable_if<optimize<typename std::decay<Ex>::type>::value>
+    ::type>
+  /* implicit */ exception_wrapper(Ex&& exn) {
+    typedef typename std::decay<Ex>::type DEx;
+    item_ = std::make_shared<DEx>(std::forward<Ex>(exn));
+    throwfn_ = folly::detail::Thrower<DEx>::doThrow;
+  }
+
+  // The following two constructors are meant to emulate the behavior of
+  // try_and_catch in performance sensitive code as well as to be flexible
+  // enough to wrap exceptions of unknown type. There is an overload that
+  // takes an exception reference so that the wrapper can extract and store
+  // the exception's type and what() when possible.
+  //
+  // The canonical use case is to construct an all-catching exception wrapper
+  // with minimal overhead like so:
+  //
+  //   try {
+  //     // some throwing code
+  //   } catch (const std::exception& e) {
+  //     // won't lose e's type and what()
+  //     exception_wrapper ew{std::current_exception(), e};
+  //   } catch (...) {
+  //     // everything else
+  //     exception_wrapper ew{std::current_exception()};
+  //   }
+  //
+  // try_and_catch is cleaner and preferable. Use it unless you're sure you need
+  // something like this instead.
+  template <typename Ex>
+  explicit exception_wrapper(std::exception_ptr eptr, Ex& exn) {
+    assign_eptr(eptr, exn);
+  }
+
+  explicit exception_wrapper(std::exception_ptr eptr) {
+    assign_eptr(eptr);
+  }
+
+  void throwException() const {
+    if (throwfn_) {
+      throwfn_(item_.get());
+    } else if (eptr_) {
+      std::rethrow_exception(eptr_);
+    }
+  }
+
+  explicit operator bool() const {
+    return item_ || eptr_;
+  }
+
+  // This implementation is similar to std::exception_ptr's implementation
+  // where two exception_wrappers are equal when the address in the underlying
+  // reference field both point to the same exception object.  The reference
+  // field remains the same when the exception_wrapper is copied or when
+  // the exception_wrapper is "rethrown".
+  bool operator==(const exception_wrapper& a) const {
+    if (item_) {
+      return a.item_ && item_.get() == a.item_.get();
+    } else {
+      return eptr_ == a.eptr_;
+    }
+  }
+
+  bool operator!=(const exception_wrapper& a) const {
+    return !(*this == a);
+  }
+
+  // This will return a non-nullptr only if the exception is held as a
+  // copy.  It is the only interface which will distinguish between an
+  // exception held this way, and by exception_ptr.  You probably
+  // shouldn't use it at all.
+  std::exception* getCopied() { return item_.get(); }
+  const std::exception* getCopied() const { return item_.get(); }
+
+  fbstring what() const {
+    if (item_) {
+      return exceptionStr(*item_);
+    } else if (eptr_) {
+      return estr_;
+    } else {
+      return fbstring();
+    }
+  }
+
+  fbstring class_name() const {
+    if (item_) {
+      auto& i = *item_;
+      return demangle(typeid(i));
+    } else if (eptr_) {
+      return ename_;
+    } else {
+      return fbstring();
+    }
+  }
+
+  template <class Ex>
+  bool is_compatible_with() const {
+    if (item_) {
+      return dynamic_cast<const Ex*>(item_.get());
+    } else if (eptr_) {
+      try {
+        std::rethrow_exception(eptr_);
+      } catch (std::exception& e) {
+        return dynamic_cast<const Ex*>(&e);
+      } catch (...) {
+        // fall through
+      }
+    }
+    return false;
+  }
+
+  // If this exception wrapper wraps an exception of type Ex, with_exception
+  // will call f with the wrapped exception as an argument and return true, and
+  // will otherwise return false.
+  template <class Ex, class F>
+  typename std::enable_if<
+    std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+    bool>::type
+  with_exception(F f) {
+    return with_exception1<typename std::decay<Ex>::type>(f, this);
+  }
+
+  // Const overload
+  template <class Ex, class F>
+  typename std::enable_if<
+    std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+    bool>::type
+  with_exception(F f) const {
+    return with_exception1<const typename std::decay<Ex>::type>(f, this);
+  }
+
+  // Overload for non-exceptions. Always rethrows.
+  template <class Ex, class F>
+  typename std::enable_if<
+    !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+    bool>::type
+  with_exception(F f) const {
+    try {
+      throwException();
+    } catch (typename std::decay<Ex>::type& e) {
+      f(e);
+      return true;
+    } catch (...) {
+      // fall through
+    }
+    return false;
+  }
+
+  std::exception_ptr getExceptionPtr() const {
+    if (eptr_) {
+      return eptr_;
+    }
+
+    try {
+      throwException();
+    } catch (...) {
+      return std::current_exception();
+    }
+    return std::exception_ptr();
+  }
+
+protected:
+  template <typename Ex>
+  struct optimize {
+    static const bool value =
+      std::is_base_of<std::exception, Ex>::value &&
+      std::is_copy_assignable<Ex>::value &&
+      !std::is_abstract<Ex>::value;
+  };
+
+  template <typename Ex>
+  void assign_eptr(std::exception_ptr eptr, Ex& e) {
+    this->eptr_ = eptr;
+    this->estr_ = exceptionStr(e).toStdString();
+    this->ename_ = demangle(typeid(e)).toStdString();
+  }
+
+  void assign_eptr(std::exception_ptr eptr) {
+    this->eptr_ = eptr;
+  }
+
+  // Optimized case: if we know what type the exception is, we can
+  // store a copy of the concrete type, and a helper function so we
+  // can rethrow it.
+  std::shared_ptr<std::exception> item_;
+  void (*throwfn_)(std::exception*){nullptr};
+  // Fallback case: store the library wrapper, which is less efficient
+  // but gets the job done.  Also store exceptionPtr() the name of the
+  // exception type, so we can at least get those back out without
+  // having to rethrow.
+  std::exception_ptr eptr_;
+  std::string estr_;
+  std::string ename_;
+
+  template <class T, class... Args>
+  friend exception_wrapper make_exception_wrapper(Args&&... args);
+
+private:
+  // What makes this useful is that T can be exception_wrapper* or
+  // const exception_wrapper*, and the compiler will use the
+  // instantiation which works with F.
+  template <class Ex, class F, class T>
+  static bool with_exception1(F f, T* that) {
+    if (that->item_) {
+      if (auto ex = dynamic_cast<Ex*>(that->item_.get())) {
+        f(*ex);
+        return true;
+      }
+    } else if (that->eptr_) {
+      try {
+        std::rethrow_exception(that->eptr_);
+      } catch (std::exception& e) {
+        if (auto ex = dynamic_cast<Ex*>(&e)) {
+          f(*ex);
+          return true;
+        }
+      } catch (...) {
+        // fall through
+      }
+    }
+    return false;
+  }
+};
+
+template <class T, class... Args>
+exception_wrapper make_exception_wrapper(Args&&... args) {
+  exception_wrapper ew;
+  ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
+  ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
+  return ew;
+}
+
+// For consistency with exceptionStr() functions in String.h
+inline fbstring exceptionStr(const exception_wrapper& ew) {
+  return ew.what();
+}
+
+/*
+ * try_and_catch is a simple replacement for try {} catch(){} that allows you to
+ * specify which derived exceptions you would like to catch and store in an
+ * exception_wrapper.
+ *
+ * Because we cannot build an equivalent of std::current_exception(), we need
+ * to catch every derived exception that we are interested in catching.
+ *
+ * Exceptions should be listed in the reverse order that you would write your
+ * catch statements (that is, std::exception& should be first).
+ *
+ * NOTE: Although implemented as a derived class (for syntactic delight), don't
+ * be confused - you should not pass around try_and_catch objects!
+ *
+ * Example Usage:
+ *
+ * // This catches my runtime_error and if I call throwException() on ew, it
+ * // will throw a runtime_error
+ * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
+ *   if (badThingHappens()) {
+ *     throw std::runtime_error("ZOMG!");
+ *   }
+ * });
+ *
+ * // This will catch the exception and if I call throwException() on ew, it
+ * // will throw a std::exception
+ * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
+ *   if (badThingHappens()) {
+ *     throw std::exception();
+ *   }
+ * });
+ *
+ * // This will not catch the exception and it will be thrown.
+ * auto ew = folly::try_and_catch<std::runtime_error>([=]() {
+ *   if (badThingHappens()) {
+ *     throw std::exception();
+ *   }
+ * });
+ */
+
+template <typename... Exceptions>
+class try_and_catch;
+
+template <typename LastException, typename... Exceptions>
+class try_and_catch<LastException, Exceptions...> :
+    public try_and_catch<Exceptions...> {
+ public:
+  template <typename F>
+  explicit try_and_catch(F&& fn) : Base() {
+    call_fn(fn);
+  }
+
+ protected:
+  typedef try_and_catch<Exceptions...> Base;
+
+  try_and_catch() : Base() {}
+
+  template <typename Ex>
+  typename std::enable_if<!exception_wrapper::optimize<Ex>::value>::type
+  assign_exception(Ex& e, std::exception_ptr eptr) {
+    exception_wrapper::assign_eptr(eptr, e);
+  }
+
+  template <typename Ex>
+  typename std::enable_if<exception_wrapper::optimize<Ex>::value>::type
+  assign_exception(Ex& e, std::exception_ptr /*eptr*/) {
+    this->item_ = std::make_shared<Ex>(e);
+    this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
+  }
+
+  template <typename F>
+  void call_fn(F&& fn) {
+    try {
+      Base::call_fn(std::move(fn));
+    } catch (LastException& e) {
+      if (typeid(e) == typeid(LastException&)) {
+        assign_exception(e, std::current_exception());
+      } else {
+        exception_wrapper::assign_eptr(std::current_exception(), e);
+      }
+    }
+  }
+};
+
+template<>
+class try_and_catch<> : public exception_wrapper {
+ public:
+  try_and_catch() = default;
+
+ protected:
+  template <typename F>
+  void call_fn(F&& fn) {
+    fn();
+  }
+};
+}
+#endif
diff --git a/faux-folly/folly/Executor.h b/faux-folly/folly/Executor.h
new file mode 100644
index 0000000..168b659
--- /dev/null
+++ b/faux-folly/folly/Executor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <climits>
+#include <functional>
+#include <stdexcept>
+
+namespace folly {
+
+typedef std::function<void()> Func;
+
+/// An Executor accepts units of work with add(), which should be
+/// threadsafe.
+class Executor {
+ public:
+  virtual ~Executor() = default;
+
+  /// Enqueue a function to executed by this executor. This and all
+  /// variants must be threadsafe.
+  virtual void add(Func) = 0;
+
+  /// Enqueue a function with a given priority, where 0 is the medium priority
+  /// This is up to the implementation to enforce
+  virtual void addWithPriority(Func, int8_t /*priority*/) {
+    throw std::runtime_error(
+        "addWithPriority() is not implemented for this Executor");
+  }
+
+  virtual uint8_t getNumPriorities() const {
+    return 1;
+  }
+
+  static const int8_t LO_PRI  = SCHAR_MIN;
+  static const int8_t MID_PRI = 0;
+  static const int8_t HI_PRI  = SCHAR_MAX;
+
+  /// A convenience function for shared_ptr to legacy functors.
+  ///
+  /// Sometimes you have a functor that is move-only, and therefore can't be
+  /// converted to a std::function (e.g. std::packaged_task). In that case,
+  /// wrap it in a shared_ptr (or maybe folly::MoveWrapper) and use this.
+  template <class P>
+  void addPtr(P fn) {
+    this->add([fn]() mutable { (*fn)(); });
+  }
+};
+
+} // folly
diff --git a/faux-folly/folly/ExecutorWithCancellation.h b/faux-folly/folly/ExecutorWithCancellation.h
new file mode 100644
index 0000000..5ffd5ca
--- /dev/null
+++ b/faux-folly/folly/ExecutorWithCancellation.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Nest Labs, 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/Executor.h>
+#include <folly/Cancellation.h>
+
+namespace folly {
+
+/**
+ * An Executor that has a single Cancellation associated with it
+ *
+ * This is a convenience interface for situations where you want an
+ * event loop Executor that has a single Cancellation object
+ * associated with it. When shutting down the Executor, the
+ * Cancellation object can be used to cancel all pending transactions.
+ */
+class ExecutorWithCancellation : virtual public Executor {
+public:
+    /**
+     * Replaces the Cancellation object, cancelling the current one.
+     *
+     * This should be called ONCE, before ever calling
+     * getCancellation(). Before setting the new Cancellation, the
+     * existing cancellation will be cancelled (thus cancelling all
+     * in-flight transactions that referred to that Cancellation
+     * object).
+     *
+     * @param cx the new Cancellation object for the Executor.
+     */
+    virtual void setCancellation(Cancellation cx) = 0;
+
+    /**
+     * Access the Cancellation object
+     *
+     * This allows the caller to access or create a copy of the
+     * Cancellation object.
+     *
+     * @return the Cancellation object that is used to cancel all
+     * transactions for this Executor.
+     */
+    virtual Cancellation& getCancellation() = 0;
+};
+
+} // folly
diff --git a/faux-folly/folly/FBString.h b/faux-folly/folly/FBString.h
new file mode 100644
index 0000000..4afce74
--- /dev/null
+++ b/faux-folly/folly/FBString.h
@@ -0,0 +1,2533 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author: Andrei Alexandrescu (aalexandre)
+// String type.
+
+#ifndef FOLLY_BASE_FBSTRING_H_
+#define FOLLY_BASE_FBSTRING_H_
+
+#include <atomic>
+#include <limits>
+#include <type_traits>
+
+// This file appears in two locations: inside fbcode and in the
+// libstdc++ source code (when embedding fbstring as std::string).
+// To aid in this schizophrenic use, _LIBSTDCXX_FBSTRING is defined in
+// libstdc++'s c++config.h, to gate use inside fbcode v. libstdc++.
+#ifdef _LIBSTDCXX_FBSTRING
+
+#pragma GCC system_header
+
+// Handle the cases where the fbcode version (folly/Malloc.h) is included
+// either before or after this inclusion.
+#ifdef FOLLY_MALLOC_H_
+#undef FOLLY_MALLOC_H_
+#include "basic_fbstring_malloc.h" // nolint
+#else
+#include "basic_fbstring_malloc.h" // nolint
+#undef FOLLY_MALLOC_H_
+#endif
+
+#else // !_LIBSTDCXX_FBSTRING
+
+#include <folly/Portability.h>
+
+// libc++ doesn't provide this header, nor does msvc
+#ifdef FOLLY_HAVE_BITS_CXXCONFIG_H
+#include <bits/c++config.h>
+#endif
+
+#include <string>
+#include <cstring>
+#include <cassert>
+#include <algorithm>
+
+#include <folly/Traits.h>
+#include <folly/Malloc.h>
+#include <folly/Hash.h>
+#include <folly/ScopeGuard.h>
+
+#if FOLLY_HAVE_DEPRECATED_ASSOC
+#ifdef _GLIBCXX_SYMVER
+#include <ext/hash_set>
+#include <ext/hash_map>
+#endif
+#endif
+
+#endif
+
+// We defined these here rather than including Likely.h to avoid
+// redefinition errors when fbstring is imported into libstdc++.
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define FBSTRING_LIKELY(x)   (__builtin_expect((x), 1))
+#define FBSTRING_UNLIKELY(x) (__builtin_expect((x), 0))
+#else
+#define FBSTRING_LIKELY(x)   (x)
+#define FBSTRING_UNLIKELY(x) (x)
+#endif
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
+// FBString cannot use throw when replacing std::string, though it may still
+// use std::__throw_*
+// nolint
+#define throw FOLLY_FBSTRING_MAY_NOT_USE_THROW
+
+#ifdef _LIBSTDCXX_FBSTRING
+namespace std _GLIBCXX_VISIBILITY(default) {
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+#else
+namespace folly {
+#endif
+
+// Different versions of gcc/clang support different versions of
+// the address sanitizer attribute.  Unfortunately, this attribute
+// has issues when inlining is used, so disable that as well.
+#if defined(__clang__)
+# if __has_feature(address_sanitizer)
+#  if __has_attribute(__no_address_safety_analysis__)
+#   define FBSTRING_DISABLE_ADDRESS_SANITIZER \
+      __attribute__((__no_address_safety_analysis__, __noinline__))
+#  elif __has_attribute(__no_sanitize_address__)
+#   define FBSTRING_DISABLE_ADDRESS_SANITIZER \
+      __attribute__((__no_sanitize_address__, __noinline__))
+#  endif
+# endif
+#elif defined (__GNUC__) && \
+      (__GNUC__ == 4) && \
+      (__GNUC_MINOR__ >= 8) && \
+      __SANITIZE_ADDRESS__
+# define FBSTRING_DISABLE_ADDRESS_SANITIZER \
+    __attribute__((__no_address_safety_analysis__, __noinline__))
+#endif
+#ifndef FBSTRING_DISABLE_ADDRESS_SANITIZER
+# define FBSTRING_DISABLE_ADDRESS_SANITIZER
+#endif
+
+namespace fbstring_detail {
+
+template <class InIt, class OutIt>
+inline
+OutIt copy_n(InIt b,
+             typename std::iterator_traits<InIt>::difference_type n,
+             OutIt d) {
+  for (; n != 0; --n, ++b, ++d) {
+    *d = *b;
+  }
+  return d;
+}
+
+template <class Pod, class T>
+inline void pod_fill(Pod* b, Pod* e, T c) {
+  assert(b && e && b <= e);
+  /*static*/ if (sizeof(T) == 1) {
+    memset(b, c, e - b);
+  } else {
+    auto const ee = b + ((e - b) & ~7u);
+    for (; b != ee; b += 8) {
+      b[0] = c;
+      b[1] = c;
+      b[2] = c;
+      b[3] = c;
+      b[4] = c;
+      b[5] = c;
+      b[6] = c;
+      b[7] = c;
+    }
+    // Leftovers
+    for (; b != e; ++b) {
+      *b = c;
+    }
+  }
+}
+
+/*
+ * Lightly structured memcpy, simplifies copying PODs and introduces
+ * some asserts. Unfortunately using this function may cause
+ * measurable overhead (presumably because it adjusts from a begin/end
+ * convention to a pointer/size convention, so it does some extra
+ * arithmetic even though the caller might have done the inverse
+ * adaptation outside).
+ */
+template <class Pod>
+inline void pod_copy(const Pod* b, const Pod* e, Pod* d) {
+  assert(e >= b);
+  assert(d >= e || d + (e - b) <= b);
+  memcpy(d, b, (e - b) * sizeof(Pod));
+}
+
+/*
+ * Lightly structured memmove, simplifies copying PODs and introduces
+ * some asserts
+ */
+template <class Pod>
+inline void pod_move(const Pod* b, const Pod* e, Pod* d) {
+  assert(e >= b);
+  memmove(d, b, (e - b) * sizeof(*b));
+}
+
+} // namespace fbstring_detail
+
+/**
+ * Defines a special acquisition method for constructing fbstring
+ * objects. AcquireMallocatedString means that the user passes a
+ * pointer to a malloc-allocated string that the fbstring object will
+ * take into custody.
+ */
+enum class AcquireMallocatedString {};
+
+/*
+ * fbstring_core_model is a mock-up type that defines all required
+ * signatures of a fbstring core. The fbstring class itself uses such
+ * a core object to implement all of the numerous member functions
+ * required by the standard.
+ *
+ * If you want to define a new core, copy the definition below and
+ * implement the primitives. Then plug the core into basic_fbstring as
+ * a template argument.
+
+template <class Char>
+class fbstring_core_model {
+public:
+  fbstring_core_model();
+  fbstring_core_model(const fbstring_core_model &);
+  ~fbstring_core_model();
+  // Returns a pointer to string's buffer (currently only contiguous
+  // strings are supported). The pointer is guaranteed to be valid
+  // until the next call to a non-const member function.
+  const Char * data() const;
+  // Much like data(), except the string is prepared to support
+  // character-level changes. This call is a signal for
+  // e.g. reference-counted implementation to fork the data. The
+  // pointer is guaranteed to be valid until the next call to a
+  // non-const member function.
+  Char * mutable_data();
+  // Returns a pointer to string's buffer and guarantees that a
+  // readable '\0' lies right after the buffer. The pointer is
+  // guaranteed to be valid until the next call to a non-const member
+  // function.
+  const Char * c_str() const;
+  // Shrinks the string by delta characters. Asserts that delta <=
+  // size().
+  void shrink(size_t delta);
+  // Expands the string by delta characters (i.e. after this call
+  // size() will report the old size() plus delta) but without
+  // initializing the expanded region. Returns a pointer to the memory
+  // to be initialized (the beginning of the expanded portion). The
+  // caller is expected to fill the expanded area appropriately.
+  Char* expand_noinit(size_t delta);
+  // Expands the string by one character and sets the last character
+  // to c.
+  void push_back(Char c);
+  // Returns the string's size.
+  size_t size() const;
+  // Returns the string's capacity, i.e. maximum size that the string
+  // can grow to without reallocation. Note that for reference counted
+  // strings that's technically a lie - even assigning characters
+  // within the existing size would cause a reallocation.
+  size_t capacity() const;
+  // Returns true if the data underlying the string is actually shared
+  // across multiple strings (in a refcounted fashion).
+  bool isShared() const;
+  // Makes sure that at least minCapacity characters are available for
+  // the string without reallocation. For reference-counted strings,
+  // it should fork the data even if minCapacity < size().
+  void reserve(size_t minCapacity);
+private:
+  // Do not implement
+  fbstring_core_model& operator=(const fbstring_core_model &);
+};
+*/
+
+/**
+ * gcc-4.7 throws what appears to be some false positive uninitialized
+ * warnings for the members of the MediumLarge struct.  So, mute them here.
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wuninitialized"
+#endif
+
+/**
+ * This is the core of the string. The code should work on 32- and
+ * 64-bit architectures and with any Char size. Porting to big endian
+ * architectures would require some changes.
+ *
+ * The storage is selected as follows (assuming we store one-byte
+ * characters on a 64-bit machine): (a) "small" strings between 0 and
+ * 23 chars are stored in-situ without allocation (the rightmost byte
+ * stores the size); (b) "medium" strings from 24 through 254 chars
+ * are stored in malloc-allocated memory that is copied eagerly; (c)
+ * "large" strings of 255 chars and above are stored in a similar
+ * structure as medium arrays, except that the string is
+ * reference-counted and copied lazily. the reference count is
+ * allocated right before the character array.
+ *
+ * The discriminator between these three strategies sits in the two
+ * most significant bits of the rightmost char of the storage. If
+ * neither is set, then the string is small (and its length sits in
+ * the lower-order bits of that rightmost character). If the MSb is
+ * set, the string is medium width. If the second MSb is set, then the
+ * string is large.
+ */
+template <class Char> class fbstring_core {
+public:
+  fbstring_core() noexcept {
+    // Only initialize the tag, will set the MSBs (i.e. the small
+    // string size) to zero too
+    ml_.capacity_ = maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)));
+    // or: setSmallSize(0);
+    writeTerminator();
+    assert(category() == Category::isSmall && size() == 0);
+  }
+
+  fbstring_core(const fbstring_core & rhs) {
+    assert(&rhs != this);
+    // Simplest case first: small strings are bitblitted
+    if (rhs.category() == Category::isSmall) {
+      static_assert(offsetof(MediumLarge, data_) == 0,
+          "fbstring layout failure");
+      static_assert(offsetof(MediumLarge, size_) == sizeof(ml_.data_),
+          "fbstring layout failure");
+      static_assert(offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_),
+          "fbstring layout failure");
+      const size_t size = rhs.smallSize();
+      if (size == 0) {
+        ml_.capacity_ = rhs.ml_.capacity_;
+        writeTerminator();
+      } else {
+        // Just write the whole thing, don't look at details. In
+        // particular we need to copy capacity anyway because we want
+        // to set the size (don't forget that the last character,
+        // which stores a short string's length, is shared with the
+        // ml_.capacity field).
+        ml_ = rhs.ml_;
+      }
+      assert(category() == Category::isSmall && this->size() == rhs.size());
+    } else if (rhs.category() == Category::isLarge) {
+      // Large strings are just refcounted
+      ml_ = rhs.ml_;
+      RefCounted::incrementRefs(ml_.data_);
+      assert(category() == Category::isLarge && size() == rhs.size());
+    } else {
+      // Medium strings are copied eagerly. Don't forget to allocate
+      // one extra Char for the null terminator.
+      auto const allocSize =
+           goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));
+      ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
+      fbstring_detail::pod_copy(rhs.ml_.data_,
+                                // 1 for terminator
+                                rhs.ml_.data_ + rhs.ml_.size_ + 1,
+                                ml_.data_);
+      // No need for writeTerminator() here, we copied one extra
+      // element just above.
+      ml_.size_ = rhs.ml_.size_;
+      ml_.capacity_ = (allocSize / sizeof(Char) - 1)
+                      | static_cast<category_type>(Category::isMedium);
+      assert(category() == Category::isMedium);
+    }
+    assert(size() == rhs.size());
+    assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
+  }
+
+  fbstring_core(fbstring_core&& goner) noexcept {
+    if (goner.category() == Category::isSmall) {
+      // Just copy, leave the goner in peace
+      new(this) fbstring_core(goner.small_, goner.smallSize());
+    } else {
+      // Take goner's guts
+      ml_ = goner.ml_;
+      // Clean goner's carcass
+      goner.setSmallSize(0);
+    }
+  }
+
+  // NOTE(agallagher): The word-aligned copy path copies bytes which are
+  // outside the range of the string, and makes address sanitizer unhappy,
+  // so just disable it on this function.
+  fbstring_core(const Char *const data, const size_t size)
+      FBSTRING_DISABLE_ADDRESS_SANITIZER {
+#ifndef NDEBUG
+#ifndef _LIBSTDCXX_FBSTRING
+    SCOPE_EXIT {
+      assert(this->size() == size);
+      assert(memcmp(this->data(), data, size * sizeof(Char)) == 0);
+    };
+#endif
+#endif
+
+    // Simplest case first: small strings are bitblitted
+    if (size <= maxSmallSize) {
+      // Layout is: Char* data_, size_t size_, size_t capacity_
+      static_assert(sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t),
+          "fbstring has unexpected size");
+      static_assert(sizeof(Char*) == sizeof(size_t),
+          "fbstring size assumption violation");
+      // sizeof(size_t) must be a power of 2
+      static_assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0,
+          "fbstring size assumption violation");
+
+      // If data is aligned, use fast word-wise copying. Otherwise,
+      // use conservative memcpy.
+      if (reinterpret_cast<size_t>(data) & (sizeof(size_t) - 1)) {
+        fbstring_detail::pod_copy(data, data + size, small_);
+      } else {
+        // Copy one word (64 bits) at a time
+        const size_t byteSize = size * sizeof(Char);
+        if (byteSize > 2 * sizeof(size_t)) {
+          // Copy three words
+          ml_.capacity_ = reinterpret_cast<const size_t*>(data)[2];
+          copyTwo:
+          ml_.size_ = reinterpret_cast<const size_t*>(data)[1];
+          copyOne:
+          ml_.data_ = *reinterpret_cast<Char**>(const_cast<Char*>(data));
+        } else if (byteSize > sizeof(size_t)) {
+          // Copy two words
+          goto copyTwo;
+        } else if (size > 0) {
+          // Copy one word
+          goto copyOne;
+        }
+      }
+      setSmallSize(size);
+      return;
+    } else if (size <= maxMediumSize) {
+      // Medium strings are allocated normally. Don't forget to
+      // allocate one extra Char for the terminating null.
+      auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));
+      ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
+      fbstring_detail::pod_copy(data, data + size, ml_.data_);
+      ml_.size_ = size;
+      ml_.capacity_ = (allocSize / sizeof(Char) - 1)
+                      | static_cast<category_type>(Category::isMedium);
+    } else {
+      // Large strings are allocated differently
+      size_t effectiveCapacity = size;
+      auto const newRC = RefCounted::create(data, & effectiveCapacity);
+      ml_.data_ = newRC->data_;
+      ml_.size_ = size;
+      ml_.capacity_ = effectiveCapacity
+                      | static_cast<category_type>(Category::isLarge);
+    }
+    writeTerminator();
+  }
+
+  ~fbstring_core() noexcept {
+    auto const c = category();
+    if (c == Category::isSmall) {
+      return;
+    }
+    if (c == Category::isMedium) {
+      free(ml_.data_);
+      return;
+    }
+    RefCounted::decrementRefs(ml_.data_);
+  }
+
+  // Snatches a previously mallocated string. The parameter "size"
+  // is the size of the string, and the parameter "allocatedSize"
+  // is the size of the mallocated block.  The string must be
+  // \0-terminated, so allocatedSize >= size + 1 and data[size] == '\0'.
+  //
+  // So if you want a 2-character string, pass malloc(3) as "data",
+  // pass 2 as "size", and pass 3 as "allocatedSize".
+  fbstring_core(Char * const data,
+                const size_t size,
+                const size_t allocatedSize,
+                AcquireMallocatedString) {
+    if (size > 0) {
+      assert(allocatedSize >= size + 1);
+      assert(data[size] == '\0');
+      // Use the medium string storage
+      ml_.data_ = data;
+      ml_.size_ = size;
+      // Don't forget about null terminator
+      ml_.capacity_ = (allocatedSize - 1)
+                      | static_cast<category_type>(Category::isMedium);
+    } else {
+      // No need for the memory
+      free(data);
+      setSmallSize(0);
+    }
+  }
+
+  // swap below doesn't test whether &rhs == this (and instead
+  // potentially does extra work) on the premise that the rarity of
+  // that situation actually makes the check more expensive than is
+  // worth.
+  void swap(fbstring_core & rhs) {
+    auto const t = ml_;
+    ml_ = rhs.ml_;
+    rhs.ml_ = t;
+  }
+
+  // In C++11 data() and c_str() are 100% equivalent.
+  const Char * data() const {
+    return c_str();
+  }
+
+  Char * mutable_data() {
+    auto const c = category();
+    if (c == Category::isSmall) {
+      return small_;
+    }
+    assert(c == Category::isMedium || c == Category::isLarge);
+    if (c == Category::isLarge && RefCounted::refs(ml_.data_) > 1) {
+      // Ensure unique.
+      size_t effectiveCapacity = ml_.capacity();
+      auto const newRC = RefCounted::create(& effectiveCapacity);
+      // If this fails, someone placed the wrong capacity in an
+      // fbstring.
+      assert(effectiveCapacity >= ml_.capacity());
+      fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
+                                newRC->data_);
+      RefCounted::decrementRefs(ml_.data_);
+      ml_.data_ = newRC->data_;
+      // No need to call writeTerminator(), we have + 1 above.
+    }
+    return ml_.data_;
+  }
+
+  const Char * c_str() const {
+    auto const c = category();
+    if (c == Category::isSmall) {
+      assert(small_[smallSize()] == '\0');
+      return small_;
+    }
+    assert(c == Category::isMedium || c == Category::isLarge);
+    assert(ml_.data_[ml_.size_] == '\0');
+    return ml_.data_;
+  }
+
+  void shrink(const size_t delta) {
+    if (category() == Category::isSmall) {
+      // Check for underflow
+      assert(delta <= smallSize());
+      setSmallSize(smallSize() - delta);
+    } else if (category() == Category::isMedium ||
+               RefCounted::refs(ml_.data_) == 1) {
+      // Medium strings and unique large strings need no special
+      // handling.
+      assert(ml_.size_ >= delta);
+      ml_.size_ -= delta;
+      writeTerminator();
+    } else {
+      assert(ml_.size_ >= delta);
+      // Shared large string, must make unique. This is because of the
+      // durn terminator must be written, which may trample the shared
+      // data.
+      if (delta) {
+        fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this);
+      }
+      // No need to write the terminator.
+    }
+  }
+
+  void reserve(size_t minCapacity) {
+    if (category() == Category::isLarge) {
+      // Ensure unique
+      if (RefCounted::refs(ml_.data_) > 1) {
+        // We must make it unique regardless; in-place reallocation is
+        // useless if the string is shared. In order to not surprise
+        // people, reserve the new block at current capacity or
+        // more. That way, a string's capacity never shrinks after a
+        // call to reserve.
+        minCapacity = std::max(minCapacity, ml_.capacity());
+        auto const newRC = RefCounted::create(& minCapacity);
+        fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
+                                   newRC->data_);
+        // Done with the old data. No need to call writeTerminator(),
+        // we have + 1 above.
+        RefCounted::decrementRefs(ml_.data_);
+        ml_.data_ = newRC->data_;
+        ml_.capacity_ = minCapacity
+                        | static_cast<category_type>(Category::isLarge);
+        // size remains unchanged
+      } else {
+        // String is not shared, so let's try to realloc (if needed)
+        if (minCapacity > ml_.capacity()) {
+          // Asking for more memory
+          auto const newRC =
+               RefCounted::reallocate(ml_.data_, ml_.size_,
+                                      ml_.capacity(), minCapacity);
+          ml_.data_ = newRC->data_;
+          ml_.capacity_ = minCapacity
+                          | static_cast<category_type>(Category::isLarge);
+          writeTerminator();
+        }
+        assert(capacity() >= minCapacity);
+      }
+    } else if (category() == Category::isMedium) {
+      // String is not shared
+      if (minCapacity <= ml_.capacity()) {
+        return; // nothing to do, there's enough room
+      }
+      if (minCapacity <= maxMediumSize) {
+        // Keep the string at medium size. Don't forget to allocate
+        // one extra Char for the terminating null.
+        size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char));
+        ml_.data_ = static_cast<Char *>(
+          smartRealloc(
+            ml_.data_,
+            ml_.size_ * sizeof(Char),
+            (ml_.capacity() + 1) * sizeof(Char),
+            capacityBytes));
+        writeTerminator();
+        ml_.capacity_ = (capacityBytes / sizeof(Char) - 1)
+                        | static_cast<category_type>(Category::isMedium);
+      } else {
+        // Conversion from medium to large string
+        fbstring_core nascent;
+        // Will recurse to another branch of this function
+        nascent.reserve(minCapacity);
+        nascent.ml_.size_ = ml_.size_;
+        fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_,
+                                  nascent.ml_.data_);
+        nascent.swap(*this);
+        writeTerminator();
+        assert(capacity() >= minCapacity);
+      }
+    } else {
+      assert(category() == Category::isSmall);
+      if (minCapacity > maxMediumSize) {
+        // large
+        auto const newRC = RefCounted::create(& minCapacity);
+        auto const size = smallSize();
+        fbstring_detail::pod_copy(small_, small_ + size + 1, newRC->data_);
+        // No need for writeTerminator(), we wrote it above with + 1.
+        ml_.data_ = newRC->data_;
+        ml_.size_ = size;
+        ml_.capacity_ = minCapacity
+                        | static_cast<category_type>(Category::isLarge);
+        assert(capacity() >= minCapacity);
+      } else if (minCapacity > maxSmallSize) {
+        // medium
+        // Don't forget to allocate one extra Char for the terminating null
+        auto const allocSizeBytes =
+          goodMallocSize((1 + minCapacity) * sizeof(Char));
+        auto const data = static_cast<Char*>(checkedMalloc(allocSizeBytes));
+        auto const size = smallSize();
+        fbstring_detail::pod_copy(small_, small_ + size + 1, data);
+        // No need for writeTerminator(), we wrote it above with + 1.
+        ml_.data_ = data;
+        ml_.size_ = size;
+        ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1)
+                        | static_cast<category_type>(Category::isMedium);
+      } else {
+        // small
+        // Nothing to do, everything stays put
+      }
+    }
+    assert(capacity() >= minCapacity);
+  }
+
+  Char * expand_noinit(const size_t delta) {
+    // Strategy is simple: make room, then change size
+    assert(capacity() >= size());
+    size_t sz, newSz;
+    if (category() == Category::isSmall) {
+      sz = smallSize();
+      newSz = sz + delta;
+      if (newSz <= maxSmallSize) {
+        setSmallSize(newSz);
+        return small_ + sz;
+      }
+      reserve(newSz);
+    } else {
+      sz = ml_.size_;
+      newSz = ml_.size_ + delta;
+      if (newSz > capacity()) {
+        reserve(newSz);
+      }
+    }
+    assert(capacity() >= newSz);
+    // Category can't be small - we took care of that above
+    assert(category() == Category::isMedium || category() == Category::isLarge);
+    ml_.size_ = newSz;
+    writeTerminator();
+    assert(size() == newSz);
+    return ml_.data_ + sz;
+  }
+
+  void push_back(Char c) {
+    assert(capacity() >= size());
+    size_t sz;
+    if (category() == Category::isSmall) {
+      sz = smallSize();
+      if (sz < maxSmallSize) {
+        small_[sz] = c;
+        setSmallSize(sz + 1);
+        return;
+      }
+      reserve(maxSmallSize * 2);
+    } else {
+      sz = ml_.size_;
+      if (sz == capacity()) {  // always true for isShared()
+        reserve(1 + sz * 3 / 2);  // ensures not shared
+      }
+    }
+    assert(!isShared());
+    assert(capacity() >= sz + 1);
+    // Category can't be small - we took care of that above
+    assert(category() == Category::isMedium || category() == Category::isLarge);
+    ml_.size_ = sz + 1;
+    ml_.data_[sz] = c;
+    writeTerminator();
+  }
+
+  size_t size() const {
+    return category() == Category::isSmall ? smallSize() : ml_.size_;
+  }
+
+  size_t capacity() const {
+    switch (category()) {
+      case Category::isSmall:
+        return maxSmallSize;
+      case Category::isLarge:
+        // For large-sized strings, a multi-referenced chunk has no
+        // available capacity. This is because any attempt to append
+        // data would trigger a new allocation.
+        if (RefCounted::refs(ml_.data_) > 1) return ml_.size_;
+      default: {}
+    }
+    return ml_.capacity();
+  }
+
+  bool isShared() const {
+    return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1;
+  }
+
+  void writeTerminator() {
+    if (category() == Category::isSmall) {
+      const auto s = smallSize();
+      if (s != maxSmallSize) {
+        small_[s] = '\0';
+      }
+    } else {
+      ml_.data_[ml_.size_] = '\0';
+    }
+  }
+
+private:
+  // Disabled
+  fbstring_core & operator=(const fbstring_core & rhs);
+
+  struct MediumLarge {
+    Char * data_;
+    size_t size_;
+    size_t capacity_;
+
+    size_t capacity() const {
+      return capacity_ & capacityExtractMask;
+    }
+  };
+
+  struct RefCounted {
+    std::atomic<size_t> refCount_;
+    Char data_[1];
+
+    static RefCounted * fromData(Char * p) {
+      return static_cast<RefCounted*>(
+        static_cast<void*>(
+          static_cast<unsigned char*>(static_cast<void*>(p))
+          - sizeof(refCount_)));
+    }
+
+    static size_t refs(Char * p) {
+      return fromData(p)->refCount_.load(std::memory_order_acquire);
+    }
+
+    static void incrementRefs(Char * p) {
+      fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel);
+    }
+
+    static void decrementRefs(Char * p) {
+      auto const dis = fromData(p);
+      size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel);
+      assert(oldcnt > 0);
+      if (oldcnt == 1) {
+        free(dis);
+      }
+    }
+
+    static RefCounted * create(size_t * size) {
+      // Don't forget to allocate one extra Char for the terminating
+      // null. In this case, however, one Char is already part of the
+      // struct.
+      const size_t allocSize = goodMallocSize(
+        sizeof(RefCounted) + *size * sizeof(Char));
+      auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));
+      result->refCount_.store(1, std::memory_order_release);
+      *size = (allocSize - sizeof(RefCounted)) / sizeof(Char);
+      return result;
+    }
+
+    static RefCounted * create(const Char * data, size_t * size) {
+      const size_t effectiveSize = *size;
+      auto result = create(size);
+      fbstring_detail::pod_copy(data, data + effectiveSize, result->data_);
+      return result;
+    }
+
+    static RefCounted * reallocate(Char *const data,
+                                   const size_t currentSize,
+                                   const size_t currentCapacity,
+                                   const size_t newCapacity) {
+      assert(newCapacity > 0 && newCapacity > currentSize);
+      auto const dis = fromData(data);
+      assert(dis->refCount_.load(std::memory_order_acquire) == 1);
+      // Don't forget to allocate one extra Char for the terminating
+      // null. In this case, however, one Char is already part of the
+      // struct.
+      auto result = static_cast<RefCounted*>(
+             smartRealloc(dis,
+                          sizeof(RefCounted) + currentSize * sizeof(Char),
+                          sizeof(RefCounted) + currentCapacity * sizeof(Char),
+                          sizeof(RefCounted) + newCapacity * sizeof(Char)));
+      assert(result->refCount_.load(std::memory_order_acquire) == 1);
+      return result;
+    }
+  };
+
+  union {
+    Char small_[sizeof(MediumLarge) / sizeof(Char)];
+    MediumLarge ml_;
+  };
+
+  enum : size_t {
+    lastChar = sizeof(MediumLarge) - 1,
+    maxSmallSize = lastChar / sizeof(Char),
+    maxMediumSize = 254 / sizeof(Char),            // coincides with the small
+                                                   // bin size in dlmalloc
+    categoryExtractMask = sizeof(size_t) == 4 ? 0xC0000000 : 0xC000000000000000,
+    capacityExtractMask = ~categoryExtractMask,
+  };
+  static_assert(!(sizeof(MediumLarge) % sizeof(Char)),
+                "Corrupt memory layout for fbstring.");
+
+  typedef std::conditional<sizeof(size_t) == 4, uint32_t, uint64_t>::type
+          category_type;
+
+  enum class Category : category_type {
+    isSmall = 0,
+    isMedium = sizeof(size_t) == 4 ? 0x80000000 : 0x8000000000000000,
+    isLarge =  sizeof(size_t) == 4 ? 0x40000000 : 0x4000000000000000,
+  };
+
+  Category category() const {
+    // Assumes little endian
+    return static_cast<Category>(ml_.capacity_ & categoryExtractMask);
+  }
+
+  size_t smallSize() const {
+    assert(category() == Category::isSmall &&
+           static_cast<size_t>(small_[maxSmallSize])
+           <= static_cast<size_t>(maxSmallSize));
+    return static_cast<size_t>(maxSmallSize)
+      - static_cast<size_t>(small_[maxSmallSize]);
+  }
+
+  void setSmallSize(size_t s) {
+    // Warning: this should work with uninitialized strings too,
+    // so don't assume anything about the previous value of
+    // small_[maxSmallSize].
+    assert(s <= maxSmallSize);
+    small_[maxSmallSize] = maxSmallSize - s;
+    writeTerminator();
+  }
+};
+
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+
+#ifndef _LIBSTDCXX_FBSTRING
+/**
+ * Dummy fbstring core that uses an actual std::string. This doesn't
+ * make any sense - it's just for testing purposes.
+ */
+template <class Char>
+class dummy_fbstring_core {
+public:
+  dummy_fbstring_core() {
+  }
+  dummy_fbstring_core(const dummy_fbstring_core& another)
+      : backend_(another.backend_) {
+  }
+  dummy_fbstring_core(const Char * s, size_t n)
+      : backend_(s, n) {
+  }
+  void swap(dummy_fbstring_core & rhs) {
+    backend_.swap(rhs.backend_);
+  }
+  const Char * data() const {
+    return backend_.data();
+  }
+  Char * mutable_data() {
+    //assert(!backend_.empty());
+    return &*backend_.begin();
+  }
+  void shrink(size_t delta) {
+    assert(delta <= size());
+    backend_.resize(size() - delta);
+  }
+  Char * expand_noinit(size_t delta) {
+    auto const sz = size();
+    backend_.resize(size() + delta);
+    return backend_.data() + sz;
+  }
+  void push_back(Char c) {
+    backend_.push_back(c);
+  }
+  size_t size() const {
+    return backend_.size();
+  }
+  size_t capacity() const {
+    return backend_.capacity();
+  }
+  bool isShared() const {
+    return false;
+  }
+  void reserve(size_t minCapacity) {
+    backend_.reserve(minCapacity);
+  }
+
+private:
+  std::basic_string<Char> backend_;
+};
+#endif // !_LIBSTDCXX_FBSTRING
+
+/**
+ * This is the basic_string replacement. For conformity,
+ * basic_fbstring takes the same template parameters, plus the last
+ * one which is the core.
+ */
+#ifdef _LIBSTDCXX_FBSTRING
+template <typename E, class T, class A, class Storage>
+#else
+template <typename E,
+          class T = std::char_traits<E>,
+          class A = std::allocator<E>,
+          class Storage = fbstring_core<E> >
+#endif
+class basic_fbstring {
+
+  static void enforce(
+      bool condition,
+      void (*throw_exc)(const char*),
+      const char* msg) {
+    if (!condition) throw_exc(msg);
+  }
+
+  bool isSane() const {
+    return
+      begin() <= end() &&
+      empty() == (size() == 0) &&
+      empty() == (begin() == end()) &&
+      size() <= max_size() &&
+      capacity() <= max_size() &&
+      size() <= capacity() &&
+      begin()[size()] == '\0';
+  }
+
+  struct Invariant;
+  friend struct Invariant;
+  struct Invariant {
+#ifndef NDEBUG
+    explicit Invariant(const basic_fbstring& s) : s_(s) {
+      assert(s_.isSane());
+    }
+    ~Invariant() {
+      assert(s_.isSane());
+    }
+  private:
+    const basic_fbstring& s_;
+#else
+    explicit Invariant(const basic_fbstring&) {}
+#endif
+    Invariant& operator=(const Invariant&);
+  };
+
+public:
+  // types
+  typedef T traits_type;
+  typedef typename traits_type::char_type value_type;
+  typedef A allocator_type;
+  typedef typename A::size_type size_type;
+  typedef typename A::difference_type difference_type;
+
+  typedef typename A::reference reference;
+  typedef typename A::const_reference const_reference;
+  typedef typename A::pointer pointer;
+  typedef typename A::const_pointer const_pointer;
+
+  typedef E* iterator;
+  typedef const E* const_iterator;
+  typedef std::reverse_iterator<iterator
+#ifdef NO_ITERATOR_TRAITS
+                                , value_type
+#endif
+                                > reverse_iterator;
+  typedef std::reverse_iterator<const_iterator
+#ifdef NO_ITERATOR_TRAITS
+                                , const value_type
+#endif
+                                > const_reverse_iterator;
+
+  static const size_type npos;                     // = size_type(-1)
+
+private:
+  static void procrustes(size_type& n, size_type nmax) {
+    if (n > nmax) n = nmax;
+  }
+
+public:
+  // C++11 21.4.2 construct/copy/destroy
+  explicit basic_fbstring(const A& /*a*/ = A()) noexcept {
+  }
+
+  basic_fbstring(const basic_fbstring& str)
+      : store_(str.store_) {
+  }
+
+  // Move constructor
+  basic_fbstring(basic_fbstring&& goner) noexcept
+      : store_(std::move(goner.store_)) {
+  }
+
+#ifndef _LIBSTDCXX_FBSTRING
+  // This is defined for compatibility with std::string
+  /* implicit */ basic_fbstring(const std::string& str)
+      : store_(str.data(), str.size()) {
+  }
+#endif
+
+  basic_fbstring(const basic_fbstring& str, size_type pos,
+                 size_type n = npos, const A& a = A()) {
+    assign(str, pos, n);
+  }
+
+  /* implicit */ basic_fbstring(const value_type* s, const A& /*a*/ = A())
+      : store_(s, s
+          ? traits_type::length(s)
+          : (std::__throw_logic_error(
+                "basic_fbstring: null pointer initializer not valid"),
+             0)) {
+  }
+
+  basic_fbstring(const value_type* s, size_type n, const A& /*a*/ = A())
+      : store_(s, n) {
+  }
+
+  basic_fbstring(size_type n, value_type c, const A& /*a*/ = A()) {
+    auto const data = store_.expand_noinit(n);
+    fbstring_detail::pod_fill(data, data + n, c);
+    store_.writeTerminator();
+  }
+
+  template <class InIt>
+  basic_fbstring(InIt begin, InIt end,
+                 typename std::enable_if<
+                 !std::is_same<typename std::remove_const<InIt>::type,
+                 value_type*>::value, const A>::type & /*a*/ = A()) {
+    assign(begin, end);
+  }
+
+  // Specialization for const char*, const char*
+  basic_fbstring(const value_type* b, const value_type* e)
+      : store_(b, e - b) {
+  }
+
+  // Nonstandard constructor
+  basic_fbstring(value_type *s, size_type n, size_type c,
+                 AcquireMallocatedString a)
+      : store_(s, n, c, a) {
+  }
+
+  // Construction from initialization list
+  basic_fbstring(std::initializer_list<value_type> il) {
+    assign(il.begin(), il.end());
+  }
+
+  ~basic_fbstring() noexcept {
+  }
+
+  basic_fbstring& operator=(const basic_fbstring& lhs) {
+    if (FBSTRING_UNLIKELY(&lhs == this)) {
+      return *this;
+    }
+    auto const oldSize = size();
+    auto const srcSize = lhs.size();
+    if (capacity() >= srcSize && !store_.isShared()) {
+      // great, just copy the contents
+      if (oldSize < srcSize)
+        store_.expand_noinit(srcSize - oldSize);
+      else
+        store_.shrink(oldSize - srcSize);
+      assert(size() == srcSize);
+      fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin());
+      store_.writeTerminator();
+    } else {
+      // need to reallocate, so we may as well create a brand new string
+      basic_fbstring(lhs).swap(*this);
+    }
+    return *this;
+  }
+
+  // Move assignment
+  basic_fbstring& operator=(basic_fbstring&& goner) noexcept {
+    if (FBSTRING_UNLIKELY(&goner == this)) {
+      // Compatibility with std::basic_string<>,
+      // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.
+      return *this;
+    }
+    // No need of this anymore
+    this->~basic_fbstring();
+    // Move the goner into this
+    new(&store_) fbstring_core<E>(std::move(goner.store_));
+    return *this;
+  }
+
+#ifndef _LIBSTDCXX_FBSTRING
+  // Compatibility with std::string
+  basic_fbstring & operator=(const std::string & rhs) {
+    return assign(rhs.data(), rhs.size());
+  }
+
+  // Compatibility with std::string
+  std::string toStdString() const {
+    return std::string(data(), size());
+  }
+#else
+  // A lot of code in fbcode still uses this method, so keep it here for now.
+  const basic_fbstring& toStdString() const {
+    return *this;
+  }
+#endif
+
+  basic_fbstring& operator=(const value_type* s) {
+    return assign(s);
+  }
+
+  basic_fbstring& operator=(value_type c) {
+    if (empty()) {
+      store_.expand_noinit(1);
+    } else if (store_.isShared()) {
+      basic_fbstring(1, c).swap(*this);
+      return *this;
+    } else {
+      store_.shrink(size() - 1);
+    }
+    *store_.mutable_data() = c;
+    store_.writeTerminator();
+    return *this;
+  }
+
+  basic_fbstring& operator=(std::initializer_list<value_type> il) {
+    return assign(il.begin(), il.end());
+  }
+
+  // C++11 21.4.3 iterators:
+  iterator begin() { return store_.mutable_data(); }
+
+  const_iterator begin() const { return store_.data(); }
+
+  const_iterator cbegin() const { return begin(); }
+
+  iterator end() {
+    return store_.mutable_data() + store_.size();
+  }
+
+  const_iterator end() const {
+    return store_.data() + store_.size();
+  }
+
+  const_iterator cend() const { return end(); }
+
+  reverse_iterator rbegin() {
+    return reverse_iterator(end());
+  }
+
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+
+  const_reverse_iterator crbegin() const { return rbegin(); }
+
+  reverse_iterator rend() {
+    return reverse_iterator(begin());
+  }
+
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+
+  const_reverse_iterator crend() const { return rend(); }
+
+  // Added by C++11
+  // C++11 21.4.5, element access:
+  const value_type& front() const { return *begin(); }
+  const value_type& back() const {
+    assert(!empty());
+    // Should be begin()[size() - 1], but that branches twice
+    return *(end() - 1);
+  }
+  value_type& front() { return *begin(); }
+  value_type& back() {
+    assert(!empty());
+    // Should be begin()[size() - 1], but that branches twice
+    return *(end() - 1);
+  }
+  void pop_back() {
+    assert(!empty());
+    store_.shrink(1);
+  }
+
+  // C++11 21.4.4 capacity:
+  size_type size() const { return store_.size(); }
+
+  size_type length() const { return size(); }
+
+  size_type max_size() const {
+    return std::numeric_limits<size_type>::max();
+  }
+
+  void resize(const size_type n, const value_type c = value_type()) {
+    auto size = this->size();
+    if (n <= size) {
+      store_.shrink(size - n);
+    } else {
+      // Do this in two steps to minimize slack memory copied (see
+      // smartRealloc).
+      auto const capacity = this->capacity();
+      assert(capacity >= size);
+      if (size < capacity) {
+        auto delta = std::min(n, capacity) - size;
+        store_.expand_noinit(delta);
+        fbstring_detail::pod_fill(begin() + size, end(), c);
+        size += delta;
+        if (size == n) {
+          store_.writeTerminator();
+          return;
+        }
+        assert(size < n);
+      }
+      auto const delta = n - size;
+      store_.expand_noinit(delta);
+      fbstring_detail::pod_fill(end() - delta, end(), c);
+      store_.writeTerminator();
+    }
+    assert(this->size() == n);
+  }
+
+  size_type capacity() const { return store_.capacity(); }
+
+  void reserve(size_type res_arg = 0) {
+    enforce(res_arg <= max_size(), std::__throw_length_error, "");
+    store_.reserve(res_arg);
+  }
+
+  void shrink_to_fit() {
+    // Shrink only if slack memory is sufficiently large
+    if (capacity() < size() * 3 / 2) {
+      return;
+    }
+    basic_fbstring(cbegin(), cend()).swap(*this);
+  }
+
+  void clear() { resize(0); }
+
+  bool empty() const { return size() == 0; }
+
+  // C++11 21.4.5 element access:
+  const_reference operator[](size_type pos) const {
+    return *(begin() + pos);
+  }
+
+  reference operator[](size_type pos) {
+    return *(begin() + pos);
+  }
+
+  const_reference at(size_type n) const {
+    enforce(n <= size(), std::__throw_out_of_range, "");
+    return (*this)[n];
+  }
+
+  reference at(size_type n) {
+    enforce(n < size(), std::__throw_out_of_range, "");
+    return (*this)[n];
+  }
+
+  // C++11 21.4.6 modifiers:
+  basic_fbstring& operator+=(const basic_fbstring& str) {
+    return append(str);
+  }
+
+  basic_fbstring& operator+=(const value_type* s) {
+    return append(s);
+  }
+
+  basic_fbstring& operator+=(const value_type c) {
+    push_back(c);
+    return *this;
+  }
+
+  basic_fbstring& operator+=(std::initializer_list<value_type> il) {
+    append(il);
+    return *this;
+  }
+
+  basic_fbstring& append(const basic_fbstring& str) {
+#ifndef NDEBUG
+    auto desiredSize = size() + str.size();
+#endif
+    append(str.data(), str.size());
+    assert(size() == desiredSize);
+    return *this;
+  }
+
+  basic_fbstring& append(const basic_fbstring& str, const size_type pos,
+                         size_type n) {
+    const size_type sz = str.size();
+    enforce(pos <= sz, std::__throw_out_of_range, "");
+    procrustes(n, sz - pos);
+    return append(str.data() + pos, n);
+  }
+
+  basic_fbstring& append(const value_type* s, size_type n) {
+#ifndef NDEBUG
+    Invariant checker(*this);
+    (void) checker;
+#endif
+    if (FBSTRING_UNLIKELY(!n)) {
+      // Unlikely but must be done
+      return *this;
+    }
+    auto const oldSize = size();
+    auto const oldData = data();
+    // Check for aliasing (rare). We could use "<=" here but in theory
+    // those do not work for pointers unless the pointers point to
+    // elements in the same array. For that reason we use
+    // std::less_equal, which is guaranteed to offer a total order
+    // over pointers. See discussion at http://goo.gl/Cy2ya for more
+    // info.
+    std::less_equal<const value_type*> le;
+    if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
+      assert(le(s + n, oldData + oldSize));
+      const size_type offset = s - oldData;
+      store_.reserve(oldSize + n);
+      // Restore the source
+      s = data() + offset;
+    }
+    // Warning! Repeated appends with short strings may actually incur
+    // practically quadratic performance. Avoid that by pushing back
+    // the first character (which ensures exponential growth) and then
+    // appending the rest normally. Worst case the append may incur a
+    // second allocation but that will be rare.
+    push_back(*s++);
+    --n;
+    memcpy(store_.expand_noinit(n), s, n * sizeof(value_type));
+    assert(size() == oldSize + n + 1);
+    return *this;
+  }
+
+  basic_fbstring& append(const value_type* s) {
+    return append(s, traits_type::length(s));
+  }
+
+  basic_fbstring& append(size_type n, value_type c) {
+    resize(size() + n, c);
+    return *this;
+  }
+
+  template<class InputIterator>
+  basic_fbstring& append(InputIterator first, InputIterator last) {
+    insert(end(), first, last);
+    return *this;
+  }
+
+  basic_fbstring& append(std::initializer_list<value_type> il) {
+    return append(il.begin(), il.end());
+  }
+
+  void push_back(const value_type c) {             // primitive
+    store_.push_back(c);
+  }
+
+  basic_fbstring& assign(const basic_fbstring& str) {
+    if (&str == this) return *this;
+    return assign(str.data(), str.size());
+  }
+
+  basic_fbstring& assign(basic_fbstring&& str) {
+    return *this = std::move(str);
+  }
+
+  basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
+                         size_type n) {
+    const size_type sz = str.size();
+    enforce(pos <= sz, std::__throw_out_of_range, "");
+    procrustes(n, sz - pos);
+    return assign(str.data() + pos, n);
+  }
+
+  basic_fbstring& assign(const value_type* s, const size_type n) {
+    Invariant checker(*this);
+    (void) checker;
+    if (size() >= n) {
+      std::copy(s, s + n, begin());
+      resize(n);
+      assert(size() == n);
+    } else {
+      const value_type *const s2 = s + size();
+      std::copy(s, s2, begin());
+      append(s2, n - size());
+      assert(size() == n);
+    }
+    store_.writeTerminator();
+    assert(size() == n);
+    return *this;
+  }
+
+  basic_fbstring& assign(const value_type* s) {
+    return assign(s, traits_type::length(s));
+  }
+
+  basic_fbstring& assign(std::initializer_list<value_type> il) {
+    return assign(il.begin(), il.end());
+  }
+
+  template <class ItOrLength, class ItOrChar>
+  basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {
+    return replace(begin(), end(), first_or_n, last_or_c);
+  }
+
+  basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {
+    return insert(pos1, str.data(), str.size());
+  }
+
+  basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
+                         size_type pos2, size_type n) {
+    enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
+    procrustes(n, str.length() - pos2);
+    return insert(pos1, str.data() + pos2, n);
+  }
+
+  basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {
+    enforce(pos <= length(), std::__throw_out_of_range, "");
+    insert(begin() + pos, s, s + n);
+    return *this;
+  }
+
+  basic_fbstring& insert(size_type pos, const value_type* s) {
+    return insert(pos, s, traits_type::length(s));
+  }
+
+  basic_fbstring& insert(size_type pos, size_type n, value_type c) {
+    enforce(pos <= length(), std::__throw_out_of_range, "");
+    insert(begin() + pos, n, c);
+    return *this;
+  }
+
+  iterator insert(const_iterator p, const value_type c) {
+    const size_type pos = p - begin();
+    insert(p, 1, c);
+    return begin() + pos;
+  }
+
+private:
+  template <int i> class Selector {};
+
+  iterator insertImplDiscr(const_iterator p,
+                           size_type n, value_type c, Selector<1>) {
+    Invariant checker(*this);
+    (void) checker;
+    auto const pos = p - begin();
+    assert(p >= begin() && p <= end());
+    if (capacity() - size() < n) {
+      const size_type sz = p - begin();
+      reserve(size() + n);
+      p = begin() + sz;
+    }
+    const iterator oldEnd = end();
+    if (n < size_type(oldEnd - p)) {
+      append(oldEnd - n, oldEnd);
+      //std::copy(
+      //    reverse_iterator(oldEnd - n),
+      //    reverse_iterator(p),
+      //    reverse_iterator(oldEnd));
+      fbstring_detail::pod_move(&*p, &*oldEnd - n,
+                                begin() + pos + n);
+      std::fill(begin() + pos, begin() + pos + n, c);
+    } else {
+      append(n - (end() - p), c);
+      append(iterator(p), oldEnd);
+      std::fill(iterator(p), oldEnd, c);
+    }
+    store_.writeTerminator();
+    return begin() + pos;
+  }
+
+  template<class InputIter>
+  iterator insertImplDiscr(const_iterator i,
+                           InputIter b, InputIter e, Selector<0>) {
+    return insertImpl(i, b, e,
+               typename std::iterator_traits<InputIter>::iterator_category());
+  }
+
+  template <class FwdIterator>
+  iterator insertImpl(const_iterator i,
+                  FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
+    Invariant checker(*this);
+    (void) checker;
+    const size_type pos = i - begin();
+    const typename std::iterator_traits<FwdIterator>::difference_type n2 =
+      std::distance(s1, s2);
+    assert(n2 >= 0);
+    using namespace fbstring_detail;
+    assert(pos <= size());
+
+    const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
+      capacity() - size();
+    if (maxn2 < n2) {
+      // realloc the string
+      reserve(size() + n2);
+      i = begin() + pos;
+    }
+    if (pos + n2 <= size()) {
+      const iterator tailBegin = end() - n2;
+      store_.expand_noinit(n2);
+      fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2);
+      std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i),
+                reverse_iterator(tailBegin + n2));
+      std::copy(s1, s2, begin() + pos);
+    } else {
+      FwdIterator t = s1;
+      const size_type old_size = size();
+      std::advance(t, old_size - pos);
+      const size_t newElems = std::distance(t, s2);
+      store_.expand_noinit(n2);
+      std::copy(t, s2, begin() + old_size);
+      fbstring_detail::pod_copy(data() + pos, data() + old_size,
+                                 begin() + old_size + newElems);
+      std::copy(s1, t, begin() + pos);
+    }
+    store_.writeTerminator();
+    return begin() + pos;
+  }
+
+  template <class InputIterator>
+  iterator insertImpl(const_iterator i,
+                      InputIterator b, InputIterator e,
+                      std::input_iterator_tag) {
+    const auto pos = i - begin();
+    basic_fbstring temp(begin(), i);
+    for (; b != e; ++b) {
+      temp.push_back(*b);
+    }
+    temp.append(i, cend());
+    swap(temp);
+    return begin() + pos;
+  }
+
+public:
+  template <class ItOrLength, class ItOrChar>
+  iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
+    Selector<std::numeric_limits<ItOrLength>::is_specialized> sel;
+    return insertImplDiscr(p, first_or_n, last_or_c, sel);
+  }
+
+  iterator insert(const_iterator p, std::initializer_list<value_type> il) {
+    return insert(p, il.begin(), il.end());
+  }
+
+  basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
+    Invariant checker(*this);
+    (void) checker;
+    enforce(pos <= length(), std::__throw_out_of_range, "");
+    procrustes(n, length() - pos);
+    std::copy(begin() + pos + n, end(), begin() + pos);
+    resize(length() - n);
+    return *this;
+  }
+
+  iterator erase(iterator position) {
+    const size_type pos(position - begin());
+    enforce(pos <= size(), std::__throw_out_of_range, "");
+    erase(pos, 1);
+    return begin() + pos;
+  }
+
+  iterator erase(iterator first, iterator last) {
+    const size_type pos(first - begin());
+    erase(pos, last - first);
+    return begin() + pos;
+  }
+
+  // Replaces at most n1 chars of *this, starting with pos1 with the
+  // content of str
+  basic_fbstring& replace(size_type pos1, size_type n1,
+                          const basic_fbstring& str) {
+    return replace(pos1, n1, str.data(), str.size());
+  }
+
+  // Replaces at most n1 chars of *this, starting with pos1,
+  // with at most n2 chars of str starting with pos2
+  basic_fbstring& replace(size_type pos1, size_type n1,
+                          const basic_fbstring& str,
+                          size_type pos2, size_type n2) {
+    enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
+    return replace(pos1, n1, str.data() + pos2,
+                   std::min(n2, str.size() - pos2));
+  }
+
+  // Replaces at most n1 chars of *this, starting with pos, with chars from s
+  basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
+    return replace(pos, n1, s, traits_type::length(s));
+  }
+
+  // Replaces at most n1 chars of *this, starting with pos, with n2
+  // occurrences of c
+  //
+  // consolidated with
+  //
+  // Replaces at most n1 chars of *this, starting with pos, with at
+  // most n2 chars of str.  str must have at least n2 chars.
+  template <class StrOrLength, class NumOrChar>
+  basic_fbstring& replace(size_type pos, size_type n1,
+                          StrOrLength s_or_n2, NumOrChar n_or_c) {
+    Invariant checker(*this);
+    (void) checker;
+    enforce(pos <= size(), std::__throw_out_of_range, "");
+    procrustes(n1, length() - pos);
+    const iterator b = begin() + pos;
+    return replace(b, b + n1, s_or_n2, n_or_c);
+  }
+
+  basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {
+    return replace(i1, i2, str.data(), str.length());
+  }
+
+  basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
+    return replace(i1, i2, s, traits_type::length(s));
+  }
+
+private:
+  basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
+                                   const value_type* s, size_type n,
+                                   Selector<2>) {
+    assert(i1 <= i2);
+    assert(begin() <= i1 && i1 <= end());
+    assert(begin() <= i2 && i2 <= end());
+    return replace(i1, i2, s, s + n);
+  }
+
+  basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
+                                   size_type n2, value_type c, Selector<1>) {
+    const size_type n1 = i2 - i1;
+    if (n1 > n2) {
+      std::fill(i1, i1 + n2, c);
+      erase(i1 + n2, i2);
+    } else {
+      std::fill(i1, i2, c);
+      insert(i2, n2 - n1, c);
+    }
+    assert(isSane());
+    return *this;
+  }
+
+  template <class InputIter>
+  basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
+                                   InputIter b, InputIter e,
+                                   Selector<0>) {
+    replaceImpl(i1, i2, b, e,
+                typename std::iterator_traits<InputIter>::iterator_category());
+    return *this;
+  }
+
+private:
+  template <class FwdIterator>
+  bool replaceAliased(iterator i1, iterator i2,
+                      FwdIterator s1, FwdIterator s2, std::false_type) {
+    return false;
+  }
+
+  template <class FwdIterator>
+  bool replaceAliased(iterator i1, iterator i2,
+                      FwdIterator s1, FwdIterator s2, std::true_type) {
+    static const std::less_equal<const value_type*> le =
+      std::less_equal<const value_type*>();
+    const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());
+    if (!aliased) {
+      return false;
+    }
+    // Aliased replace, copy to new string
+    basic_fbstring temp;
+    temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));
+    temp.append(begin(), i1).append(s1, s2).append(i2, end());
+    swap(temp);
+    return true;
+  }
+
+  template <class FwdIterator>
+  void replaceImpl(iterator i1, iterator i2,
+                   FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
+    Invariant checker(*this);
+    (void) checker;
+
+    // Handle aliased replace
+    if (replaceAliased(i1, i2, s1, s2,
+          std::integral_constant<bool,
+            std::is_same<FwdIterator, iterator>::value ||
+            std::is_same<FwdIterator, const_iterator>::value>())) {
+      return;
+    }
+
+    auto const n1 = i2 - i1;
+    assert(n1 >= 0);
+    auto const n2 = std::distance(s1, s2);
+    assert(n2 >= 0);
+
+    if (n1 > n2) {
+      // shrinks
+      std::copy(s1, s2, i1);
+      erase(i1 + n2, i2);
+    } else {
+      // grows
+      fbstring_detail::copy_n(s1, n1, i1);
+      std::advance(s1, n1);
+      insert(i2, s1, s2);
+    }
+    assert(isSane());
+  }
+
+  template <class InputIterator>
+  void replaceImpl(iterator i1, iterator i2,
+                   InputIterator b, InputIterator e, std::input_iterator_tag) {
+    basic_fbstring temp(begin(), i1);
+    temp.append(b, e).append(i2, end());
+    swap(temp);
+  }
+
+public:
+  template <class T1, class T2>
+  basic_fbstring& replace(iterator i1, iterator i2,
+                          T1 first_or_n_or_s, T2 last_or_c_or_n) {
+    const bool
+      num1 = std::numeric_limits<T1>::is_specialized,
+      num2 = std::numeric_limits<T2>::is_specialized;
+    return replaceImplDiscr(
+      i1, i2, first_or_n_or_s, last_or_c_or_n,
+      Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>());
+  }
+
+  size_type copy(value_type* s, size_type n, size_type pos = 0) const {
+    enforce(pos <= size(), std::__throw_out_of_range, "");
+    procrustes(n, size() - pos);
+
+    fbstring_detail::pod_copy(
+      data() + pos,
+      data() + pos + n,
+      s);
+    return n;
+  }
+
+  void swap(basic_fbstring& rhs) {
+    store_.swap(rhs.store_);
+  }
+
+  const value_type* c_str() const {
+    return store_.c_str();
+  }
+
+  const value_type* data() const { return c_str(); }
+
+  allocator_type get_allocator() const {
+    return allocator_type();
+  }
+
+  size_type find(const basic_fbstring& str, size_type pos = 0) const {
+    return find(str.data(), pos, str.length());
+  }
+
+  size_type find(const value_type* needle, const size_type pos,
+                 const size_type nsize) const {
+    if (!nsize) return pos;
+    auto const size = this->size();
+    // nsize + pos can overflow (eg pos == npos), guard against that by checking
+    // that nsize + pos does not wrap around.
+    if (nsize + pos > size || nsize + pos < pos) return npos;
+    // Don't use std::search, use a Boyer-Moore-like trick by comparing
+    // the last characters first
+    auto const haystack = data();
+    auto const nsize_1 = nsize - 1;
+    auto const lastNeedle = needle[nsize_1];
+
+    // Boyer-Moore skip value for the last char in the needle. Zero is
+    // not a valid value; skip will be computed the first time it's
+    // needed.
+    size_type skip = 0;
+
+    const E * i = haystack + pos;
+    auto iEnd = haystack + size - nsize_1;
+
+    while (i < iEnd) {
+      // Boyer-Moore: match the last element in the needle
+      while (i[nsize_1] != lastNeedle) {
+        if (++i == iEnd) {
+          // not found
+          return npos;
+        }
+      }
+      // Here we know that the last char matches
+      // Continue in pedestrian mode
+      for (size_t j = 0; ; ) {
+        assert(j < nsize);
+        if (i[j] != needle[j]) {
+          // Not found, we can skip
+          // Compute the skip value lazily
+          if (skip == 0) {
+            skip = 1;
+            while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {
+              ++skip;
+            }
+          }
+          i += skip;
+          break;
+        }
+        // Check if done searching
+        if (++j == nsize) {
+          // Yay
+          return i - haystack;
+        }
+      }
+    }
+    return npos;
+  }
+
+  size_type find(const value_type* s, size_type pos = 0) const {
+    return find(s, pos, traits_type::length(s));
+  }
+
+  size_type find (value_type c, size_type pos = 0) const {
+    return find(&c, pos, 1);
+  }
+
+  size_type rfind(const basic_fbstring& str, size_type pos = npos) const {
+    return rfind(str.data(), pos, str.length());
+  }
+
+  size_type rfind(const value_type* s, size_type pos, size_type n) const {
+    if (n > length()) return npos;
+    pos = std::min(pos, length() - n);
+    if (n == 0) return pos;
+
+    const_iterator i(begin() + pos);
+    for (; ; --i) {
+      if (traits_type::eq(*i, *s)
+          && traits_type::compare(&*i, s, n) == 0) {
+        return i - begin();
+      }
+      if (i == begin()) break;
+    }
+    return npos;
+  }
+
+  size_type rfind(const value_type* s, size_type pos = npos) const {
+    return rfind(s, pos, traits_type::length(s));
+  }
+
+  size_type rfind(value_type c, size_type pos = npos) const {
+    return rfind(&c, pos, 1);
+  }
+
+  size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {
+    return find_first_of(str.data(), pos, str.length());
+  }
+
+  size_type find_first_of(const value_type* s,
+                          size_type pos, size_type n) const {
+    if (pos > length() || n == 0) return npos;
+    const_iterator i(begin() + pos),
+      finish(end());
+    for (; i != finish; ++i) {
+      if (traits_type::find(s, n, *i) != 0) {
+        return i - begin();
+      }
+    }
+    return npos;
+  }
+
+  size_type find_first_of(const value_type* s, size_type pos = 0) const {
+    return find_first_of(s, pos, traits_type::length(s));
+  }
+
+  size_type find_first_of(value_type c, size_type pos = 0) const {
+    return find_first_of(&c, pos, 1);
+  }
+
+  size_type find_last_of (const basic_fbstring& str,
+                          size_type pos = npos) const {
+    return find_last_of(str.data(), pos, str.length());
+  }
+
+  size_type find_last_of (const value_type* s, size_type pos,
+                          size_type n) const {
+    if (!empty() && n > 0) {
+      pos = std::min(pos, length() - 1);
+      const_iterator i(begin() + pos);
+      for (;; --i) {
+        if (traits_type::find(s, n, *i) != 0) {
+          return i - begin();
+        }
+        if (i == begin()) break;
+      }
+    }
+    return npos;
+  }
+
+  size_type find_last_of (const value_type* s,
+                          size_type pos = npos) const {
+    return find_last_of(s, pos, traits_type::length(s));
+  }
+
+  size_type find_last_of (value_type c, size_type pos = npos) const {
+    return find_last_of(&c, pos, 1);
+  }
+
+  size_type find_first_not_of(const basic_fbstring& str,
+                              size_type pos = 0) const {
+    return find_first_not_of(str.data(), pos, str.size());
+  }
+
+  size_type find_first_not_of(const value_type* s, size_type pos,
+                              size_type n) const {
+    if (pos < length()) {
+      const_iterator
+        i(begin() + pos),
+        finish(end());
+      for (; i != finish; ++i) {
+        if (traits_type::find(s, n, *i) == 0) {
+          return i - begin();
+        }
+      }
+    }
+    return npos;
+  }
+
+  size_type find_first_not_of(const value_type* s,
+                              size_type pos = 0) const {
+    return find_first_not_of(s, pos, traits_type::length(s));
+  }
+
+  size_type find_first_not_of(value_type c, size_type pos = 0) const {
+    return find_first_not_of(&c, pos, 1);
+  }
+
+  size_type find_last_not_of(const basic_fbstring& str,
+                             size_type pos = npos) const {
+    return find_last_not_of(str.data(), pos, str.length());
+  }
+
+  size_type find_last_not_of(const value_type* s, size_type pos,
+                             size_type n) const {
+    if (!this->empty()) {
+      pos = std::min(pos, size() - 1);
+      const_iterator i(begin() + pos);
+      for (;; --i) {
+        if (traits_type::find(s, n, *i) == 0) {
+          return i - begin();
+        }
+        if (i == begin()) break;
+      }
+    }
+    return npos;
+  }
+
+  size_type find_last_not_of(const value_type* s,
+                             size_type pos = npos) const {
+    return find_last_not_of(s, pos, traits_type::length(s));
+  }
+
+  size_type find_last_not_of (value_type c, size_type pos = npos) const {
+    return find_last_not_of(&c, pos, 1);
+  }
+
+  basic_fbstring substr(size_type pos = 0, size_type n = npos) const& {
+    enforce(pos <= size(), std::__throw_out_of_range, "");
+    return basic_fbstring(data() + pos, std::min(n, size() - pos));
+  }
+
+  basic_fbstring substr(size_type pos = 0, size_type n = npos) && {
+    enforce(pos <= size(), std::__throw_out_of_range, "");
+    erase(0, pos);
+    if (n < size()) resize(n);
+    return std::move(*this);
+  }
+
+  int compare(const basic_fbstring& str) const {
+    // FIX due to Goncalo N M de Carvalho July 18, 2005
+    return compare(0, size(), str);
+  }
+
+  int compare(size_type pos1, size_type n1,
+              const basic_fbstring& str) const {
+    return compare(pos1, n1, str.data(), str.size());
+  }
+
+  int compare(size_type pos1, size_type n1,
+              const value_type* s) const {
+    return compare(pos1, n1, s, traits_type::length(s));
+  }
+
+  int compare(size_type pos1, size_type n1,
+              const value_type* s, size_type n2) const {
+    enforce(pos1 <= size(), std::__throw_out_of_range, "");
+    procrustes(n1, size() - pos1);
+    // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!
+    const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));
+    return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
+  }
+
+  int compare(size_type pos1, size_type n1,
+              const basic_fbstring& str,
+              size_type pos2, size_type n2) const {
+    enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
+    return compare(pos1, n1, str.data() + pos2,
+                   std::min(n2, str.size() - pos2));
+  }
+
+  // Code from Jean-Francois Bastien (03/26/2007)
+  int compare(const value_type* s) const {
+    // Could forward to compare(0, size(), s, traits_type::length(s))
+    // but that does two extra checks
+    const size_type n1(size()), n2(traits_type::length(s));
+    const int r = traits_type::compare(data(), s, std::min(n1, n2));
+    return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
+  }
+
+private:
+  // Data
+  Storage store_;
+};
+
+// non-member functions
+// C++11 21.4.8.1/1
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
+                                     const basic_fbstring<E, T, A, S>& rhs) {
+
+  basic_fbstring<E, T, A, S> result;
+  result.reserve(lhs.size() + rhs.size());
+  result.append(lhs).append(rhs);
+  return std::move(result);
+}
+
+// C++11 21.4.8.1/2
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
+                                     const basic_fbstring<E, T, A, S>& rhs) {
+  return std::move(lhs.append(rhs));
+}
+
+// C++11 21.4.8.1/3
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
+                                     basic_fbstring<E, T, A, S>&& rhs) {
+  if (rhs.capacity() >= lhs.size() + rhs.size()) {
+    // Good, at least we don't need to reallocate
+    return std::move(rhs.insert(0, lhs));
+  }
+  // Meh, no go. Forward to operator+(const&, const&).
+  auto const& rhsC = rhs;
+  return lhs + rhsC;
+}
+
+// C++11 21.4.8.1/4
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
+                                     basic_fbstring<E, T, A, S>&& rhs) {
+  return std::move(lhs.append(rhs));
+}
+
+// C++11 21.4.8.1/5
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  const E* lhs,
+  const basic_fbstring<E, T, A, S>& rhs) {
+  //
+  basic_fbstring<E, T, A, S> result;
+  const auto len = basic_fbstring<E, T, A, S>::traits_type::length(lhs);
+  result.reserve(len + rhs.size());
+  result.append(lhs, len).append(rhs);
+  return result;
+}
+
+// C++11 21.4.8.1/6
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  const E* lhs,
+  basic_fbstring<E, T, A, S>&& rhs) {
+  //
+  const auto len = basic_fbstring<E, T, A, S>::traits_type::length(lhs);
+  if (rhs.capacity() >= len + rhs.size()) {
+    // Good, at least we don't need to reallocate
+    return std::move(rhs.insert(rhs.begin(), lhs, lhs + len));
+  }
+  // Meh, no go. Do it by hand since we have len already.
+  basic_fbstring<E, T, A, S> result;
+  result.reserve(len + rhs.size());
+  result.append(lhs, len).append(rhs);
+  return result;
+}
+
+// C++11 21.4.8.1/7
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  E lhs,
+  const basic_fbstring<E, T, A, S>& rhs) {
+
+  basic_fbstring<E, T, A, S> result;
+  result.reserve(1 + rhs.size());
+  result.push_back(lhs);
+  result.append(rhs);
+  return result;
+}
+
+// C++11 21.4.8.1/8
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  E lhs,
+  basic_fbstring<E, T, A, S>&& rhs) {
+  //
+  if (rhs.capacity() > rhs.size()) {
+    // Good, at least we don't need to reallocate
+    return std::move(rhs.insert(rhs.begin(), lhs));
+  }
+  // Meh, no go. Forward to operator+(E, const&).
+  auto const& rhsC = rhs;
+  return lhs + rhsC;
+}
+
+// C++11 21.4.8.1/9
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  const basic_fbstring<E, T, A, S>& lhs,
+  const E* rhs) {
+
+  typedef typename basic_fbstring<E, T, A, S>::size_type size_type;
+  typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;
+
+  basic_fbstring<E, T, A, S> result;
+  const size_type len = traits_type::length(rhs);
+  result.reserve(lhs.size() + len);
+  result.append(lhs).append(rhs, len);
+  return result;
+}
+
+// C++11 21.4.8.1/10
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  basic_fbstring<E, T, A, S>&& lhs,
+  const E* rhs) {
+  //
+  return std::move(lhs += rhs);
+}
+
+// C++11 21.4.8.1/11
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  const basic_fbstring<E, T, A, S>& lhs,
+  E rhs) {
+
+  basic_fbstring<E, T, A, S> result;
+  result.reserve(lhs.size() + 1);
+  result.append(lhs);
+  result.push_back(rhs);
+  return result;
+}
+
+// C++11 21.4.8.1/12
+template <typename E, class T, class A, class S>
+inline
+basic_fbstring<E, T, A, S> operator+(
+  basic_fbstring<E, T, A, S>&& lhs,
+  E rhs) {
+  //
+  return std::move(lhs += rhs);
+}
+
+template <typename E, class T, class A, class S>
+inline
+bool operator==(const basic_fbstring<E, T, A, S>& lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator==(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs == lhs; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator==(const basic_fbstring<E, T, A, S>& lhs,
+                const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
+  return lhs.compare(rhs) == 0; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return !(lhs == rhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator!=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return !(lhs == rhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
+                const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
+  return !(lhs == rhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator<(const basic_fbstring<E, T, A, S>& lhs,
+               const basic_fbstring<E, T, A, S>& rhs) {
+  return lhs.compare(rhs) < 0; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator<(const basic_fbstring<E, T, A, S>& lhs,
+               const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
+  return lhs.compare(rhs) < 0; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator<(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
+               const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs.compare(lhs) > 0; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator>(const basic_fbstring<E, T, A, S>& lhs,
+               const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs < lhs; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator>(const basic_fbstring<E, T, A, S>& lhs,
+               const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
+  return rhs < lhs; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator>(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
+               const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs < lhs; }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return !(rhs < lhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
+                const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
+  return !(rhs < lhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator<=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return !(rhs < lhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return !(lhs < rhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
+                const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
+  return !(lhs < rhs); }
+
+template <typename E, class T, class A, class S>
+inline
+bool operator>=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+ return !(lhs < rhs);
+}
+
+// C++11 21.4.8.8
+template <typename E, class T, class A, class S>
+void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {
+  lhs.swap(rhs);
+}
+
+// TODO: make this faster.
+template <typename E, class T, class A, class S>
+inline
+std::basic_istream<
+  typename basic_fbstring<E, T, A, S>::value_type,
+  typename basic_fbstring<E, T, A, S>::traits_type>&
+  operator>>(
+    std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
+    typename basic_fbstring<E, T, A, S>::traits_type>& is,
+    basic_fbstring<E, T, A, S>& str) {
+  typename std::basic_istream<E, T>::sentry sentry(is);
+  typedef std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
+                             typename basic_fbstring<E, T, A, S>::traits_type>
+                        __istream_type;
+  typedef typename __istream_type::ios_base __ios_base;
+  size_t extracted = 0;
+  auto err = __ios_base::goodbit;
+  if (sentry) {
+    auto n = is.width();
+    if (n <= 0) {
+      n = str.max_size();
+    }
+    str.erase();
+    for (auto got = is.rdbuf()->sgetc(); extracted != size_t(n); ++extracted) {
+      if (got == T::eof()) {
+        err |= __ios_base::eofbit;
+        is.width(0);
+        break;
+      }
+      if (isspace(got)) break;
+      str.push_back(got);
+      got = is.rdbuf()->snextc();
+    }
+  }
+  if (!extracted) {
+    err |= __ios_base::failbit;
+  }
+  if (err) {
+    is.setstate(err);
+  }
+  return is;
+}
+
+template <typename E, class T, class A, class S>
+inline
+std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
+                   typename basic_fbstring<E, T, A, S>::traits_type>&
+operator<<(
+  std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
+  typename basic_fbstring<E, T, A, S>::traits_type>& os,
+    const basic_fbstring<E, T, A, S>& str) {
+#if _LIBCPP_VERSION
+  typename std::basic_ostream<
+    typename basic_fbstring<E, T, A, S>::value_type,
+    typename basic_fbstring<E, T, A, S>::traits_type>::sentry __s(os);
+  if (__s) {
+    typedef std::ostreambuf_iterator<
+      typename basic_fbstring<E, T, A, S>::value_type,
+      typename basic_fbstring<E, T, A, S>::traits_type> _Ip;
+    size_t __len = str.size();
+    bool __left =
+      (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
+    if (__pad_and_output(_Ip(os),
+                         str.data(),
+                         __left ? str.data() + __len : str.data(),
+                         str.data() + __len,
+                         os,
+                         os.fill()).failed()) {
+      os.setstate(std::ios_base::badbit | std::ios_base::failbit);
+    }
+  }
+#elif defined(_MSC_VER)
+  // MSVC doesn't define __ostream_insert
+  os.write(str.data(), str.size());
+#else
+  std::__ostream_insert(os, str.data(), str.size());
+#endif
+  return os;
+}
+
+#ifndef _LIBSTDCXX_FBSTRING
+
+template <typename E, class T, class A, class S>
+inline
+std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
+                   typename basic_fbstring<E, T, A, S>::traits_type>&
+getline(
+  std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
+  typename basic_fbstring<E, T, A, S>::traits_type>& is,
+    basic_fbstring<E, T, A, S>& str,
+  typename basic_fbstring<E, T, A, S>::value_type delim) {
+  // Use the nonstandard getdelim()
+  char * buf = nullptr;
+  size_t size = 0;
+  for (;;) {
+    // This looks quadratic but it really depends on realloc
+    auto const newSize = size + 128;
+    buf = static_cast<char*>(checkedRealloc(buf, newSize));
+    is.getline(buf + size, newSize - size, delim);
+    if (is.bad() || is.eof() || !is.fail()) {
+      // done by either failure, end of file, or normal read
+      size += std::strlen(buf + size);
+      break;
+    }
+    // Here we have failed due to too short a buffer
+    // Minus one to discount the terminating '\0'
+    size = newSize - 1;
+    assert(buf[size] == 0);
+    // Clear the error so we can continue reading
+    is.clear();
+  }
+  basic_fbstring<E, T, A, S> result(buf, size, size + 1,
+                                    AcquireMallocatedString());
+  result.swap(str);
+  return is;
+}
+
+template <typename E, class T, class A, class S>
+inline
+std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
+                   typename basic_fbstring<E, T, A, S>::traits_type>&
+getline(
+  std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
+  typename basic_fbstring<E, T, A, S>::traits_type>& is,
+  basic_fbstring<E, T, A, S>& str) {
+  // Just forward to the version with a delimiter
+  return getline(is, str, '\n');
+}
+
+#endif
+
+template <typename E1, class T, class A, class S>
+const typename basic_fbstring<E1, T, A, S>::size_type
+basic_fbstring<E1, T, A, S>::npos =
+              static_cast<typename basic_fbstring<E1, T, A, S>::size_type>(-1);
+
+#ifndef _LIBSTDCXX_FBSTRING
+// basic_string compatibility routines
+
+template <typename E, class T, class A, class S>
+inline
+bool operator==(const basic_fbstring<E, T, A, S>& lhs,
+                const std::string& rhs) {
+  return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;
+}
+
+template <typename E, class T, class A, class S>
+inline
+bool operator==(const std::string& lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs == lhs;
+}
+
+template <typename E, class T, class A, class S>
+inline
+bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
+                const std::string& rhs) {
+  return !(lhs == rhs);
+}
+
+template <typename E, class T, class A, class S>
+inline
+bool operator!=(const std::string& lhs,
+                const basic_fbstring<E, T, A, S>& rhs) {
+  return !(lhs == rhs);
+}
+
+#if !defined(_LIBSTDCXX_FBSTRING)
+typedef basic_fbstring<char> fbstring;
+#endif
+
+// fbstring is relocatable
+template <class T, class R, class A, class S>
+FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);
+
+#else
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif
+
+} // namespace folly
+
+#ifndef _LIBSTDCXX_FBSTRING
+
+// Hash functions to make fbstring usable with e.g. hash_map
+//
+// Handle interaction with different C++ standard libraries, which
+// expect these types to be in different namespaces.
+
+#define FOLLY_FBSTRING_HASH1(T) \
+  template <> \
+  struct hash< ::folly::basic_fbstring<T> > { \
+    size_t operator()(const ::folly::fbstring& s) const { \
+      return ::folly::hash::fnv32_buf(s.data(), s.size()); \
+    } \
+  };
+
+// The C++11 standard says that these four are defined
+#define FOLLY_FBSTRING_HASH \
+  FOLLY_FBSTRING_HASH1(char) \
+  FOLLY_FBSTRING_HASH1(char16_t) \
+  FOLLY_FBSTRING_HASH1(char32_t) \
+  FOLLY_FBSTRING_HASH1(wchar_t)
+
+namespace std {
+
+FOLLY_FBSTRING_HASH
+
+}  // namespace std
+
+#if FOLLY_HAVE_DEPRECATED_ASSOC
+#if defined(_GLIBCXX_SYMVER) && !defined(__BIONIC__)
+namespace __gnu_cxx {
+
+FOLLY_FBSTRING_HASH
+
+}  // namespace __gnu_cxx
+#endif // _GLIBCXX_SYMVER && !__BIONIC__
+#endif // FOLLY_HAVE_DEPRECATED_ASSOC
+
+#undef FOLLY_FBSTRING_HASH
+#undef FOLLY_FBSTRING_HASH1
+
+#endif // _LIBSTDCXX_FBSTRING
+
+#pragma GCC diagnostic pop
+
+#undef FBSTRING_DISABLE_ADDRESS_SANITIZER
+#undef throw
+#undef FBSTRING_LIKELY
+#undef FBSTRING_UNLIKELY
+
+#endif // FOLLY_BASE_FBSTRING_H_
diff --git a/faux-folly/folly/FBVector.h b/faux-folly/folly/FBVector.h
new file mode 100644
index 0000000..22d7603
--- /dev/null
+++ b/faux-folly/folly/FBVector.h
@@ -0,0 +1,1646 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ * Nicholas Ormrod      (njormrod)
+ * Andrei Alexandrescu  (aalexandre)
+ *
+ * FBVector is Facebook's drop-in implementation of std::vector. It has special
+ * optimizations for use with relocatable types and jemalloc.
+ */
+
+#ifndef FOLLY_FBVECTOR_H
+#define FOLLY_FBVECTOR_H
+
+//=============================================================================
+// headers
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+#include <folly/FormatTraits.h>
+#include <folly/Likely.h>
+#include <folly/Malloc.h>
+#include <folly/Traits.h>
+
+#include <boost/operators.hpp>
+
+//=============================================================================
+// forward declaration
+
+namespace folly {
+  template <class T, class Allocator = std::allocator<T>>
+  class fbvector;
+}
+
+//=============================================================================
+// unrolling
+
+#define FOLLY_FBV_UNROLL_PTR(first, last, OP) do {  \
+  for (; (last) - (first) >= 4; (first) += 4) {     \
+    OP(((first) + 0));                              \
+    OP(((first) + 1));                              \
+    OP(((first) + 2));                              \
+    OP(((first) + 3));                              \
+  }                                                 \
+  for (; (first) != (last); ++(first)) OP((first)); \
+} while(0);
+
+//=============================================================================
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+//                              fbvector class                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+namespace folly {
+
+template <class T, class Allocator>
+class fbvector : private boost::totally_ordered<fbvector<T, Allocator>> {
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // implementation
+private:
+
+  typedef std::allocator_traits<Allocator> A;
+
+  struct Impl : public Allocator {
+    // typedefs
+    typedef typename A::pointer pointer;
+    typedef typename A::size_type size_type;
+
+    // data
+    pointer b_, e_, z_;
+
+    // constructors
+    Impl() : Allocator(), b_(nullptr), e_(nullptr), z_(nullptr) {}
+    /* implicit */ Impl(const Allocator& a)
+      : Allocator(a), b_(nullptr), e_(nullptr), z_(nullptr) {}
+    /* implicit */ Impl(Allocator&& a)
+      : Allocator(std::move(a)), b_(nullptr), e_(nullptr), z_(nullptr) {}
+
+    /* implicit */ Impl(size_type n, const Allocator& a = Allocator())
+      : Allocator(a)
+      { init(n); }
+
+    Impl(Impl&& other) noexcept
+      : Allocator(std::move(other)),
+        b_(other.b_), e_(other.e_), z_(other.z_)
+      { other.b_ = other.e_ = other.z_ = nullptr; }
+
+    // destructor
+    ~Impl() {
+      destroy();
+    }
+
+    // allocation
+    // note that 'allocate' and 'deallocate' are inherited from Allocator
+    T* D_allocate(size_type n) {
+      if (usingStdAllocator::value) {
+        return static_cast<T*>(malloc(n * sizeof(T)));
+      } else {
+        return std::allocator_traits<Allocator>::allocate(*this, n);
+      }
+    }
+
+    void D_deallocate(T* p, size_type n) noexcept {
+      if (usingStdAllocator::value) {
+        free(p);
+      } else {
+        std::allocator_traits<Allocator>::deallocate(*this, p, n);
+      }
+    }
+
+    // helpers
+    void swapData(Impl& other) {
+      std::swap(b_, other.b_);
+      std::swap(e_, other.e_);
+      std::swap(z_, other.z_);
+    }
+
+    // data ops
+    inline void destroy() noexcept {
+      if (b_) {
+        // THIS DISPATCH CODE IS DUPLICATED IN fbvector::D_destroy_range_a.
+        // It has been inlined here for speed. It calls the static fbvector
+        //  methods to perform the actual destruction.
+        if (usingStdAllocator::value) {
+          S_destroy_range(b_, e_);
+        } else {
+          S_destroy_range_a(*this, b_, e_);
+        }
+
+        D_deallocate(b_, z_ - b_);
+      }
+    }
+
+    void init(size_type n) {
+      if (UNLIKELY(n == 0)) {
+        b_ = e_ = z_ = nullptr;
+      } else {
+        size_type sz = folly::goodMallocSize(n * sizeof(T)) / sizeof(T);
+        b_ = D_allocate(sz);
+        e_ = b_;
+        z_ = b_ + sz;
+      }
+    }
+
+    void
+    set(pointer newB, size_type newSize, size_type newCap) {
+      z_ = newB + newCap;
+      e_ = newB + newSize;
+      b_ = newB;
+    }
+
+    void reset(size_type newCap) {
+      destroy();
+      try {
+        init(newCap);
+      } catch (...) {
+        init(0);
+        throw;
+      }
+    }
+    void reset() { // same as reset(0)
+      destroy();
+      b_ = e_ = z_ = nullptr;
+    }
+  } impl_;
+
+  static void swap(Impl& a, Impl& b) {
+    using std::swap;
+    if (!usingStdAllocator::value) swap<Allocator>(a, b);
+    a.swapData(b);
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // types and constants
+public:
+
+  typedef T                                           value_type;
+  typedef value_type&                                 reference;
+  typedef const value_type&                           const_reference;
+  typedef T*                                          iterator;
+  typedef const T*                                    const_iterator;
+  typedef size_t                                      size_type;
+  typedef typename std::make_signed<size_type>::type  difference_type;
+  typedef Allocator                                   allocator_type;
+  typedef typename A::pointer                         pointer;
+  typedef typename A::const_pointer                   const_pointer;
+  typedef std::reverse_iterator<iterator>             reverse_iterator;
+  typedef std::reverse_iterator<const_iterator>       const_reverse_iterator;
+
+private:
+
+  typedef std::integral_constant<bool,
+      boost::has_trivial_copy_constructor<T>::value &&
+      sizeof(T) <= 16 // don't force large structures to be passed by value
+    > should_pass_by_value;
+  typedef typename std::conditional<
+      should_pass_by_value::value, T, const T&>::type VT;
+  typedef typename std::conditional<
+      should_pass_by_value::value, T, T&&>::type MT;
+
+  typedef std::integral_constant<bool,
+      std::is_same<Allocator, std::allocator<T>>::value> usingStdAllocator;
+  typedef std::integral_constant<bool,
+      usingStdAllocator::value ||
+      A::propagate_on_container_move_assignment::value> moveIsSwap;
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // allocator helpers
+private:
+
+  //---------------------------------------------------------------------------
+  // allocate
+
+  T* M_allocate(size_type n) {
+    return impl_.D_allocate(n);
+  }
+
+  //---------------------------------------------------------------------------
+  // deallocate
+
+  void M_deallocate(T* p, size_type n) noexcept {
+    impl_.D_deallocate(p, n);
+  }
+
+  //---------------------------------------------------------------------------
+  // construct
+
+  // GCC is very sensitive to the exact way that construct is called. For
+  //  that reason there are several different specializations of construct.
+
+  template <typename U, typename... Args>
+  void M_construct(U* p, Args&&... args) {
+    if (usingStdAllocator::value) {
+      new (p) U(std::forward<Args>(args)...);
+    } else {
+      std::allocator_traits<Allocator>::construct(
+        impl_, p, std::forward<Args>(args)...);
+    }
+  }
+
+  template <typename U, typename... Args>
+  static void S_construct(U* p, Args&&... args) {
+    new (p) U(std::forward<Args>(args)...);
+  }
+
+  template <typename U, typename... Args>
+  static void S_construct_a(Allocator& a, U* p, Args&&... args) {
+    std::allocator_traits<Allocator>::construct(
+      a, p, std::forward<Args>(args)...);
+  }
+
+  // scalar optimization
+  // TODO we can expand this optimization to: default copyable and assignable
+  template <typename U, typename Enable = typename
+    std::enable_if<std::is_scalar<U>::value>::type>
+  void M_construct(U* p, U arg) {
+    if (usingStdAllocator::value) {
+      *p = arg;
+    } else {
+      std::allocator_traits<Allocator>::construct(impl_, p, arg);
+    }
+  }
+
+  template <typename U, typename Enable = typename
+    std::enable_if<std::is_scalar<U>::value>::type>
+  static void S_construct(U* p, U arg) {
+    *p = arg;
+  }
+
+  template <typename U, typename Enable = typename
+    std::enable_if<std::is_scalar<U>::value>::type>
+  static void S_construct_a(Allocator& a, U* p, U arg) {
+    std::allocator_traits<Allocator>::construct(a, p, arg);
+  }
+
+  // const& optimization
+  template <typename U, typename Enable = typename
+    std::enable_if<!std::is_scalar<U>::value>::type>
+  void M_construct(U* p, const U& value) {
+    if (usingStdAllocator::value) {
+      new (p) U(value);
+    } else {
+      std::allocator_traits<Allocator>::construct(impl_, p, value);
+    }
+  }
+
+  template <typename U, typename Enable = typename
+    std::enable_if<!std::is_scalar<U>::value>::type>
+  static void S_construct(U* p, const U& value) {
+    new (p) U(value);
+  }
+
+  template <typename U, typename Enable = typename
+    std::enable_if<!std::is_scalar<U>::value>::type>
+  static void S_construct_a(Allocator& a, U* p, const U& value) {
+    std::allocator_traits<Allocator>::construct(a, p, value);
+  }
+
+  //---------------------------------------------------------------------------
+  // destroy
+
+  void M_destroy(T* p) noexcept {
+    if (usingStdAllocator::value) {
+      if (!boost::has_trivial_destructor<T>::value) p->~T();
+    } else {
+      std::allocator_traits<Allocator>::destroy(impl_, p);
+    }
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // algorithmic helpers
+private:
+
+  //---------------------------------------------------------------------------
+  // destroy_range
+
+  // wrappers
+  void M_destroy_range_e(T* pos) noexcept {
+    D_destroy_range_a(pos, impl_.e_);
+    impl_.e_ = pos;
+  }
+
+  // dispatch
+  // THIS DISPATCH CODE IS DUPLICATED IN IMPL. SEE IMPL FOR DETAILS.
+  void D_destroy_range_a(T* first, T* last) noexcept {
+    if (usingStdAllocator::value) {
+      S_destroy_range(first, last);
+    } else {
+      S_destroy_range_a(impl_, first, last);
+    }
+  }
+
+  // allocator
+  static void S_destroy_range_a(Allocator& a, T* first, T* last) noexcept {
+    for (; first != last; ++first)
+      std::allocator_traits<Allocator>::destroy(a, first);
+  }
+
+  // optimized
+  static void S_destroy_range(T* first, T* last) noexcept {
+    if (!boost::has_trivial_destructor<T>::value) {
+      // EXPERIMENTAL DATA on fbvector<vector<int>> (where each vector<int> has
+      //  size 0).
+      // The unrolled version seems to work faster for small to medium sized
+      //  fbvectors. It gets a 10% speedup on fbvectors of size 1024, 64, and
+      //  16.
+      // The simple loop version seems to work faster for large fbvectors. The
+      //  unrolled version is about 6% slower on fbvectors on size 16384.
+      // The two methods seem tied for very large fbvectors. The unrolled
+      //  version is about 0.5% slower on size 262144.
+
+      // for (; first != last; ++first) first->~T();
+      #define FOLLY_FBV_OP(p) (p)->~T()
+      FOLLY_FBV_UNROLL_PTR(first, last, FOLLY_FBV_OP)
+      #undef FOLLY_FBV_OP
+    }
+  }
+
+  //---------------------------------------------------------------------------
+  // uninitialized_fill_n
+
+  // wrappers
+  void M_uninitialized_fill_n_e(size_type sz) {
+    D_uninitialized_fill_n_a(impl_.e_, sz);
+    impl_.e_ += sz;
+  }
+
+  void M_uninitialized_fill_n_e(size_type sz, VT value) {
+    D_uninitialized_fill_n_a(impl_.e_, sz, value);
+    impl_.e_ += sz;
+  }
+
+  // dispatch
+  void D_uninitialized_fill_n_a(T* dest, size_type sz) {
+    if (usingStdAllocator::value) {
+      S_uninitialized_fill_n(dest, sz);
+    } else {
+      S_uninitialized_fill_n_a(impl_, dest, sz);
+    }
+  }
+
+  void D_uninitialized_fill_n_a(T* dest, size_type sz, VT value) {
+    if (usingStdAllocator::value) {
+      S_uninitialized_fill_n(dest, sz, value);
+    } else {
+      S_uninitialized_fill_n_a(impl_, dest, sz, value);
+    }
+  }
+
+  // allocator
+  template <typename... Args>
+  static void S_uninitialized_fill_n_a(Allocator& a, T* dest,
+                                       size_type sz, Args&&... args) {
+    auto b = dest;
+    auto e = dest + sz;
+    try {
+      for (; b != e; ++b)
+        std::allocator_traits<Allocator>::construct(a, b,
+          std::forward<Args>(args)...);
+    } catch (...) {
+      S_destroy_range_a(a, dest, b);
+      throw;
+    }
+  }
+
+  // optimized
+  static void S_uninitialized_fill_n(T* dest, size_type n) {
+    if (folly::IsZeroInitializable<T>::value) {
+      std::memset(dest, 0, sizeof(T) * n);
+    } else {
+      auto b = dest;
+      auto e = dest + n;
+      try {
+        for (; b != e; ++b) S_construct(b);
+      } catch (...) {
+        --b;
+        for (; b >= dest; --b) b->~T();
+        throw;
+      }
+    }
+  }
+
+  static void S_uninitialized_fill_n(T* dest, size_type n, const T& value) {
+    auto b = dest;
+    auto e = dest + n;
+    try {
+      for (; b != e; ++b) S_construct(b, value);
+    } catch (...) {
+      S_destroy_range(dest, b);
+      throw;
+    }
+  }
+
+  //---------------------------------------------------------------------------
+  // uninitialized_copy
+
+  // it is possible to add an optimization for the case where
+  // It = move(T*) and IsRelocatable<T> and Is0Initiailizable<T>
+
+  // wrappers
+  template <typename It>
+  void M_uninitialized_copy_e(It first, It last) {
+    D_uninitialized_copy_a(impl_.e_, first, last);
+    impl_.e_ += std::distance(first, last);
+  }
+
+  template <typename It>
+  void M_uninitialized_move_e(It first, It last) {
+    D_uninitialized_move_a(impl_.e_, first, last);
+    impl_.e_ += std::distance(first, last);
+  }
+
+  // dispatch
+  template <typename It>
+  void D_uninitialized_copy_a(T* dest, It first, It last) {
+    if (usingStdAllocator::value) {
+      if (folly::IsTriviallyCopyable<T>::value) {
+        S_uninitialized_copy_bits(dest, first, last);
+      } else {
+        S_uninitialized_copy(dest, first, last);
+      }
+    } else {
+      S_uninitialized_copy_a(impl_, dest, first, last);
+    }
+  }
+
+  template <typename It>
+  void D_uninitialized_move_a(T* dest, It first, It last) {
+    D_uninitialized_copy_a(dest,
+      std::make_move_iterator(first), std::make_move_iterator(last));
+  }
+
+  // allocator
+  template <typename It>
+  static void
+  S_uninitialized_copy_a(Allocator& a, T* dest, It first, It last) {
+    auto b = dest;
+    try {
+      for (; first != last; ++first, ++b)
+        std::allocator_traits<Allocator>::construct(a, b, *first);
+    } catch (...) {
+      S_destroy_range_a(a, dest, b);
+      throw;
+    }
+  }
+
+  // optimized
+  template <typename It>
+  static void S_uninitialized_copy(T* dest, It first, It last) {
+    auto b = dest;
+    try {
+      for (; first != last; ++first, ++b)
+        S_construct(b, *first);
+    } catch (...) {
+      S_destroy_range(dest, b);
+      throw;
+    }
+  }
+
+  static void
+  S_uninitialized_copy_bits(T* dest, const T* first, const T* last) {
+    std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T));
+  }
+
+  static void
+  S_uninitialized_copy_bits(T* dest, std::move_iterator<T*> first,
+                       std::move_iterator<T*> last) {
+    T* bFirst = first.base();
+    T* bLast = last.base();
+    std::memcpy((void*)dest, (void*)bFirst, (bLast - bFirst) * sizeof(T));
+  }
+
+  template <typename It>
+  static void
+  S_uninitialized_copy_bits(T* dest, It first, It last) {
+    S_uninitialized_copy(dest, first, last);
+  }
+
+  //---------------------------------------------------------------------------
+  // copy_n
+
+  // This function is "unsafe": it assumes that the iterator can be advanced at
+  //  least n times. However, as a private function, that unsafety is managed
+  //  wholly by fbvector itself.
+
+  template <typename It>
+  static It S_copy_n(T* dest, It first, size_type n) {
+    auto e = dest + n;
+    for (; dest != e; ++dest, ++first) *dest = *first;
+    return first;
+  }
+
+  static const T* S_copy_n(T* dest, const T* first, size_type n) {
+    if (folly::IsTriviallyCopyable<T>::value) {
+      std::memcpy((void*)dest, (void*)first, n * sizeof(T));
+      return first + n;
+    } else {
+      return S_copy_n<const T*>(dest, first, n);
+    }
+  }
+
+  static std::move_iterator<T*>
+  S_copy_n(T* dest, std::move_iterator<T*> mIt, size_type n) {
+    if (folly::IsTriviallyCopyable<T>::value) {
+      T* first = mIt.base();
+      std::memcpy((void*)dest, (void*)first, n * sizeof(T));
+      return std::make_move_iterator(first + n);
+    } else {
+      return S_copy_n<std::move_iterator<T*>>(dest, mIt, n);
+    }
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // relocation helpers
+private:
+
+  // Relocation is divided into three parts:
+  //
+  //  1: relocate_move
+  //     Performs the actual movement of data from point a to point b.
+  //
+  //  2: relocate_done
+  //     Destroys the old data.
+  //
+  //  3: relocate_undo
+  //     Destoys the new data and restores the old data.
+  //
+  // The three steps are used because there may be an exception after part 1
+  //  has completed. If that is the case, then relocate_undo can nullify the
+  //  initial move. Otherwise, relocate_done performs the last bit of tidying
+  //  up.
+  //
+  // The relocation trio may use either memcpy, move, or copy. It is decided
+  //  by the following case statement:
+  //
+  //  IsRelocatable && usingStdAllocator    -> memcpy
+  //  has_nothrow_move && usingStdAllocator -> move
+  //  cannot copy                           -> move
+  //  default                               -> copy
+  //
+  // If the class is non-copyable then it must be movable. However, if the
+  //  move constructor is not noexcept, i.e. an error could be thrown, then
+  //  relocate_undo will be unable to restore the old data, for fear of a
+  //  second exception being thrown. This is a known and unavoidable
+  //  deficiency. In lieu of a strong exception guarantee, relocate_undo does
+  //  the next best thing: it provides a weak exception guarantee by
+  //  destorying the new data, but leaving the old data in an indeterminate
+  //  state. Note that that indeterminate state will be valid, since the
+  //  old data has not been destroyed; it has merely been the source of a
+  //  move, which is required to leave the source in a valid state.
+
+  // wrappers
+  void M_relocate(T* newB) {
+    relocate_move(newB, impl_.b_, impl_.e_);
+    relocate_done(newB, impl_.b_, impl_.e_);
+  }
+
+  // dispatch type trait
+  typedef std::integral_constant<bool,
+      folly::IsRelocatable<T>::value && usingStdAllocator::value
+    > relocate_use_memcpy;
+
+  typedef std::integral_constant<bool,
+      (std::is_nothrow_move_constructible<T>::value
+       && usingStdAllocator::value)
+      || !std::is_copy_constructible<T>::value
+    > relocate_use_move;
+
+  // move
+  void relocate_move(T* dest, T* first, T* last) {
+    relocate_move_or_memcpy(dest, first, last, relocate_use_memcpy());
+  }
+
+  void relocate_move_or_memcpy(T* dest, T* first, T* last, std::true_type) {
+    std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T));
+  }
+
+  void relocate_move_or_memcpy(T* dest, T* first, T* last, std::false_type) {
+    relocate_move_or_copy(dest, first, last, relocate_use_move());
+  }
+
+  void relocate_move_or_copy(T* dest, T* first, T* last, std::true_type) {
+    D_uninitialized_move_a(dest, first, last);
+  }
+
+  void relocate_move_or_copy(T* dest, T* first, T* last, std::false_type) {
+    D_uninitialized_copy_a(dest, first, last);
+  }
+
+  // done
+  void relocate_done(T* /*dest*/, T* first, T* last) noexcept {
+    if (folly::IsRelocatable<T>::value && usingStdAllocator::value) {
+      // used memcpy; data has been relocated, do not call destructor
+    } else {
+      D_destroy_range_a(first, last);
+    }
+  }
+
+  // undo
+  void relocate_undo(T* dest, T* first, T* last) noexcept {
+    if (folly::IsRelocatable<T>::value && usingStdAllocator::value) {
+      // used memcpy, old data is still valid, nothing to do
+    } else if (std::is_nothrow_move_constructible<T>::value &&
+               usingStdAllocator::value) {
+      // noexcept move everything back, aka relocate_move
+      relocate_move(first, dest, dest + (last - first));
+    } else if (!std::is_copy_constructible<T>::value) {
+      // weak guarantee
+      D_destroy_range_a(dest, dest + (last - first));
+    } else {
+      // used copy, old data is still valid
+      D_destroy_range_a(dest, dest + (last - first));
+    }
+  }
+
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // construct/copy/destroy
+public:
+
+  fbvector() = default;
+
+  explicit fbvector(const Allocator& a) : impl_(a) {}
+
+  explicit fbvector(size_type n, const Allocator& a = Allocator())
+    : impl_(n, a)
+    { M_uninitialized_fill_n_e(n); }
+
+  fbvector(size_type n, VT value, const Allocator& a = Allocator())
+    : impl_(n, a)
+    { M_uninitialized_fill_n_e(n, value); }
+
+  template <class It, class Category = typename
+            std::iterator_traits<It>::iterator_category>
+  fbvector(It first, It last, const Allocator& a = Allocator())
+    : fbvector(first, last, a, Category()) {}
+
+  fbvector(const fbvector& other)
+    : impl_(other.size(), A::select_on_container_copy_construction(other.impl_))
+    { M_uninitialized_copy_e(other.begin(), other.end()); }
+
+  fbvector(fbvector&& other) noexcept : impl_(std::move(other.impl_)) {}
+
+  fbvector(const fbvector& other, const Allocator& a)
+    : fbvector(other.begin(), other.end(), a) {}
+
+  /* may throw */ fbvector(fbvector&& other, const Allocator& a) : impl_(a) {
+    if (impl_ == other.impl_) {
+      impl_.swapData(other.impl_);
+    } else {
+      impl_.init(other.size());
+      M_uninitialized_move_e(other.begin(), other.end());
+    }
+  }
+
+  fbvector(std::initializer_list<T> il, const Allocator& a = Allocator())
+    : fbvector(il.begin(), il.end(), a) {}
+
+  ~fbvector() = default; // the cleanup occurs in impl_
+
+  fbvector& operator=(const fbvector& other) {
+    if (UNLIKELY(this == &other)) return *this;
+
+    if (!usingStdAllocator::value &&
+        A::propagate_on_container_copy_assignment::value) {
+      if (impl_ != other.impl_) {
+        // can't use other's different allocator to clean up self
+        impl_.reset();
+      }
+      (Allocator&)impl_ = (Allocator&)other.impl_;
+    }
+
+    assign(other.begin(), other.end());
+    return *this;
+  }
+
+  fbvector& operator=(fbvector&& other) {
+    if (UNLIKELY(this == &other)) return *this;
+    moveFrom(std::move(other), moveIsSwap());
+    return *this;
+  }
+
+  fbvector& operator=(std::initializer_list<T> il) {
+    assign(il.begin(), il.end());
+    return *this;
+  }
+
+  template <class It, class Category = typename
+            std::iterator_traits<It>::iterator_category>
+  void assign(It first, It last) {
+    assign(first, last, Category());
+  }
+
+  void assign(size_type n, VT value) {
+    if (n > capacity()) {
+      // Not enough space. Do not reserve in place, since we will
+      // discard the old values anyways.
+      if (dataIsInternalAndNotVT(value)) {
+        T copy(std::move(value));
+        impl_.reset(n);
+        M_uninitialized_fill_n_e(n, copy);
+      } else {
+        impl_.reset(n);
+        M_uninitialized_fill_n_e(n, value);
+      }
+    } else if (n <= size()) {
+      auto newE = impl_.b_ + n;
+      std::fill(impl_.b_, newE, value);
+      M_destroy_range_e(newE);
+    } else {
+      std::fill(impl_.b_, impl_.e_, value);
+      M_uninitialized_fill_n_e(n - size(), value);
+    }
+  }
+
+  void assign(std::initializer_list<T> il) {
+    assign(il.begin(), il.end());
+  }
+
+  allocator_type get_allocator() const noexcept {
+    return impl_;
+  }
+
+private:
+
+  // contract dispatch for iterator types fbvector(It first, It last)
+  template <class ForwardIterator>
+  fbvector(ForwardIterator first, ForwardIterator last,
+           const Allocator& a, std::forward_iterator_tag)
+    : impl_(std::distance(first, last), a)
+    { M_uninitialized_copy_e(first, last); }
+
+  template <class InputIterator>
+  fbvector(InputIterator first, InputIterator last,
+           const Allocator& a, std::input_iterator_tag)
+    : impl_(a)
+    { for (; first != last; ++first) emplace_back(*first); }
+
+  // contract dispatch for allocator movement in operator=(fbvector&&)
+  void
+  moveFrom(fbvector&& other, std::true_type) {
+    swap(impl_, other.impl_);
+  }
+  void moveFrom(fbvector&& other, std::false_type) {
+    if (impl_ == other.impl_) {
+      impl_.swapData(other.impl_);
+    } else {
+      impl_.reset(other.size());
+      M_uninitialized_move_e(other.begin(), other.end());
+    }
+  }
+
+  // contract dispatch for iterator types in assign(It first, It last)
+  template <class ForwardIterator>
+  void assign(ForwardIterator first, ForwardIterator last,
+              std::forward_iterator_tag) {
+    const size_t newSize = std::distance(first, last);
+    if (newSize > capacity()) {
+      impl_.reset(newSize);
+      M_uninitialized_copy_e(first, last);
+    } else if (newSize <= size()) {
+      auto newEnd = std::copy(first, last, impl_.b_);
+      M_destroy_range_e(newEnd);
+    } else {
+      auto mid = S_copy_n(impl_.b_, first, size());
+      M_uninitialized_copy_e<decltype(last)>(mid, last);
+    }
+  }
+
+  template <class InputIterator>
+  void assign(InputIterator first, InputIterator last,
+              std::input_iterator_tag) {
+    auto p = impl_.b_;
+    for (; first != last && p != impl_.e_; ++first, ++p) {
+      *p = *first;
+    }
+    if (p != impl_.e_) {
+      M_destroy_range_e(p);
+    } else {
+      for (; first != last; ++first) emplace_back(*first);
+    }
+  }
+
+  // contract dispatch for aliasing under VT optimization
+  bool dataIsInternalAndNotVT(const T& t) {
+    if (should_pass_by_value::value) return false;
+    return dataIsInternal(t);
+  }
+  bool dataIsInternal(const T& t) {
+    return UNLIKELY(impl_.b_ <= std::addressof(t) &&
+                    std::addressof(t) < impl_.e_);
+  }
+
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // iterators
+public:
+
+  iterator begin() noexcept {
+    return impl_.b_;
+  }
+  const_iterator begin() const noexcept {
+    return impl_.b_;
+  }
+  iterator end() noexcept {
+    return impl_.e_;
+  }
+  const_iterator end() const noexcept {
+    return impl_.e_;
+  }
+  reverse_iterator rbegin() noexcept {
+    return reverse_iterator(end());
+  }
+  const_reverse_iterator rbegin() const noexcept {
+    return const_reverse_iterator(end());
+  }
+  reverse_iterator rend() noexcept {
+    return reverse_iterator(begin());
+  }
+  const_reverse_iterator rend() const noexcept {
+    return const_reverse_iterator(begin());
+  }
+
+  const_iterator cbegin() const noexcept {
+    return impl_.b_;
+  }
+  const_iterator cend() const noexcept {
+    return impl_.e_;
+  }
+  const_reverse_iterator crbegin() const noexcept {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator crend() const noexcept {
+    return const_reverse_iterator(begin());
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // capacity
+public:
+
+  size_type size() const noexcept {
+    return impl_.e_ - impl_.b_;
+  }
+
+  size_type max_size() const noexcept {
+    // good luck gettin' there
+    return ~size_type(0);
+  }
+
+  void resize(size_type n) {
+    if (n <= size()) {
+      M_destroy_range_e(impl_.b_ + n);
+    } else {
+      reserve(n);
+      M_uninitialized_fill_n_e(n - size());
+    }
+  }
+
+  void resize(size_type n, VT t) {
+    if (n <= size()) {
+      M_destroy_range_e(impl_.b_ + n);
+    } else if (dataIsInternalAndNotVT(t) && n > capacity()) {
+      T copy(t);
+      reserve(n);
+      M_uninitialized_fill_n_e(n - size(), copy);
+    } else {
+      reserve(n);
+      M_uninitialized_fill_n_e(n - size(), t);
+    }
+  }
+
+  size_type capacity() const noexcept {
+    return impl_.z_ - impl_.b_;
+  }
+
+  bool empty() const noexcept {
+    return impl_.b_ == impl_.e_;
+  }
+
+  void reserve(size_type n) {
+    if (n <= capacity()) return;
+    if (impl_.b_ && reserve_in_place(n)) return;
+
+    auto newCap = folly::goodMallocSize(n * sizeof(T)) / sizeof(T);
+    auto newB = M_allocate(newCap);
+    try {
+      M_relocate(newB);
+    } catch (...) {
+      M_deallocate(newB, newCap);
+      throw;
+    }
+    if (impl_.b_)
+      M_deallocate(impl_.b_, impl_.z_ - impl_.b_);
+    impl_.z_ = newB + newCap;
+    impl_.e_ = newB + (impl_.e_ - impl_.b_);
+    impl_.b_ = newB;
+  }
+
+  void shrink_to_fit() noexcept {
+    auto const newCapacityBytes = folly::goodMallocSize(size() * sizeof(T));
+    auto const newCap = newCapacityBytes / sizeof(T);
+    auto const oldCap = capacity();
+
+    if (newCap >= oldCap) return;
+
+    void* p = impl_.b_;
+    // xallocx() will shrink to precisely newCapacityBytes (which was generated
+    // by goodMallocSize()) if it successfully shrinks in place.
+    if ((usingJEMalloc() && usingStdAllocator::value) &&
+        newCapacityBytes >= folly::jemallocMinInPlaceExpandable &&
+        xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) {
+      impl_.z_ += newCap - oldCap;
+    } else {
+      T* newB; // intentionally uninitialized
+      try {
+        newB = M_allocate(newCap);
+        try {
+          M_relocate(newB);
+        } catch (...) {
+          M_deallocate(newB, newCap);
+          return; // swallow the error
+        }
+      } catch (...) {
+        return;
+      }
+      if (impl_.b_)
+        M_deallocate(impl_.b_, impl_.z_ - impl_.b_);
+      impl_.z_ = newB + newCap;
+      impl_.e_ = newB + (impl_.e_ - impl_.b_);
+      impl_.b_ = newB;
+    }
+  }
+
+private:
+
+  bool reserve_in_place(size_type n) {
+    if (!usingStdAllocator::value || !usingJEMalloc()) return false;
+
+    // jemalloc can never grow in place blocks smaller than 4096 bytes.
+    if ((impl_.z_ - impl_.b_) * sizeof(T) <
+      folly::jemallocMinInPlaceExpandable) return false;
+
+    auto const newCapacityBytes = folly::goodMallocSize(n * sizeof(T));
+    void* p = impl_.b_;
+    if (xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) {
+      impl_.z_ = impl_.b_ + newCapacityBytes / sizeof(T);
+      return true;
+    }
+    return false;
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // element access
+public:
+
+  reference operator[](size_type n) {
+    assert(n < size());
+    return impl_.b_[n];
+  }
+  const_reference operator[](size_type n) const {
+    assert(n < size());
+    return impl_.b_[n];
+  }
+  const_reference at(size_type n) const {
+    if (UNLIKELY(n >= size())) {
+      throw std::out_of_range("fbvector: index is greater than size.");
+    }
+    return (*this)[n];
+  }
+  reference at(size_type n) {
+    auto const& cThis = *this;
+    return const_cast<reference>(cThis.at(n));
+  }
+  reference front() {
+    assert(!empty());
+    return *impl_.b_;
+  }
+  const_reference front() const {
+    assert(!empty());
+    return *impl_.b_;
+  }
+  reference back()  {
+    assert(!empty());
+    return impl_.e_[-1];
+  }
+  const_reference back() const {
+    assert(!empty());
+    return impl_.e_[-1];
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // data access
+public:
+
+  T* data() noexcept {
+    return impl_.b_;
+  }
+  const T* data() const noexcept {
+    return impl_.b_;
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // modifiers (common)
+public:
+
+  template <class... Args>
+  void emplace_back(Args&&... args)  {
+    if (impl_.e_ != impl_.z_) {
+      M_construct(impl_.e_, std::forward<Args>(args)...);
+      ++impl_.e_;
+    } else {
+      emplace_back_aux(std::forward<Args>(args)...);
+    }
+  }
+
+  void
+  push_back(const T& value) {
+    if (impl_.e_ != impl_.z_) {
+      M_construct(impl_.e_, value);
+      ++impl_.e_;
+    } else {
+      emplace_back_aux(value);
+    }
+  }
+
+  void
+  push_back(T&& value) {
+    if (impl_.e_ != impl_.z_) {
+      M_construct(impl_.e_, std::move(value));
+      ++impl_.e_;
+    } else {
+      emplace_back_aux(std::move(value));
+    }
+  }
+
+  void pop_back() {
+    assert(!empty());
+    --impl_.e_;
+    M_destroy(impl_.e_);
+  }
+
+  void swap(fbvector& other) noexcept {
+    if (!usingStdAllocator::value &&
+        A::propagate_on_container_swap::value)
+      swap(impl_, other.impl_);
+    else impl_.swapData(other.impl_);
+  }
+
+  void clear() noexcept {
+    M_destroy_range_e(impl_.b_);
+  }
+
+private:
+
+  // std::vector implements a similar function with a different growth
+  //  strategy: empty() ? 1 : capacity() * 2.
+  //
+  // fbvector grows differently on two counts:
+  //
+  // (1) initial size
+  //     Instead of grwoing to size 1 from empty, and fbvector allocates at
+  //     least 64 bytes. You may still use reserve to reserve a lesser amount
+  //     of memory.
+  // (2) 1.5x
+  //     For medium-sized vectors, the growth strategy is 1.5x. See the docs
+  //     for details.
+  //     This does not apply to very small or very large fbvectors. This is a
+  //     heuristic.
+  //     A nice addition to fbvector would be the capability of having a user-
+  //     defined growth strategy, probably as part of the allocator.
+  //
+
+  size_type computePushBackCapacity() const {
+    if (capacity() == 0) {
+      return std::max(64 / sizeof(T), size_type(1));
+    }
+    if (capacity() < folly::jemallocMinInPlaceExpandable / sizeof(T)) {
+      return capacity() * 2;
+    }
+    if (capacity() > 4096 * 32 / sizeof(T)) {
+      return capacity() * 2;
+    }
+    return (capacity() * 3 + 1) / 2;
+  }
+
+  template <class... Args>
+  void emplace_back_aux(Args&&... args);
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // modifiers (erase)
+public:
+
+  iterator erase(const_iterator position) {
+    return erase(position, position + 1);
+  }
+
+  iterator erase(const_iterator first, const_iterator last) {
+    assert(isValid(first) && isValid(last));
+    assert(first <= last);
+    if (first != last) {
+      if (last == end()) {
+        M_destroy_range_e((iterator)first);
+      } else {
+        if (folly::IsRelocatable<T>::value && usingStdAllocator::value) {
+          D_destroy_range_a((iterator)first, (iterator)last);
+          if (last - first >= cend() - last) {
+            std::memcpy((void*)first, (void*)last, (cend() - last) * sizeof(T));
+          } else {
+            std::memmove((iterator)first, last, (cend() - last) * sizeof(T));
+          }
+          impl_.e_ -= (last - first);
+        } else {
+          std::copy(std::make_move_iterator((iterator)last),
+                    std::make_move_iterator(end()), (iterator)first);
+          auto newEnd = impl_.e_ - std::distance(first, last);
+          M_destroy_range_e(newEnd);
+        }
+      }
+    }
+    return (iterator)first;
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // modifiers (insert)
+private: // we have the private section first because it defines some macros
+
+  bool isValid(const_iterator it) {
+    return cbegin() <= it && it <= cend();
+  }
+
+  size_type computeInsertCapacity(size_type n) {
+    size_type nc = std::max(computePushBackCapacity(), size() + n);
+    size_type ac = folly::goodMallocSize(nc * sizeof(T)) / sizeof(T);
+    return ac;
+  }
+
+  //---------------------------------------------------------------------------
+  //
+  // make_window takes an fbvector, and creates an uninitialized gap (a
+  //  window) at the given position, of the given size. The fbvector must
+  //  have enough capacity.
+  //
+  // Explanation by picture.
+  //
+  //    123456789______
+  //        ^
+  //        make_window here of size 3
+  //
+  //    1234___56789___
+  //
+  // If something goes wrong and the window must be destroyed, use
+  //  undo_window to provide a weak exception guarantee. It destroys
+  //  the right ledge.
+  //
+  //    1234___________
+  //
+  //---------------------------------------------------------------------------
+  //
+  // wrap_frame takes an inverse window and relocates an fbvector around it.
+  //  The fbvector must have at least as many elements as the left ledge.
+  //
+  // Explanation by picture.
+  //
+  //        START
+  //    fbvector:             inverse window:
+  //    123456789______       _____abcde_______
+  //                          [idx][ n ]
+  //
+  //        RESULT
+  //    _______________       12345abcde6789___
+  //
+  //---------------------------------------------------------------------------
+  //
+  // insert_use_fresh_memory returns true iff the fbvector should use a fresh
+  //  block of memory for the insertion. If the fbvector does not have enough
+  //  spare capacity, then it must return true. Otherwise either true or false
+  //  may be returned.
+  //
+  //---------------------------------------------------------------------------
+  //
+  // These three functions, make_window, wrap_frame, and
+  //  insert_use_fresh_memory, can be combined into a uniform interface.
+  // Since that interface involves a lot of case-work, it is built into
+  //  some macros: FOLLY_FBVECTOR_INSERT_(START|TRY|END)
+  // Macros are used in an attempt to let GCC perform better optimizations,
+  //  especially control flow optimization.
+  //
+
+  //---------------------------------------------------------------------------
+  // window
+
+  void make_window(iterator position, size_type n) {
+    assert(isValid(position));
+    assert(size() + n <= capacity());
+    assert(n != 0);
+
+    // The result is guaranteed to be non-negative, so use an unsigned type:
+    size_type tail = std::distance(position, impl_.e_);
+
+    if (tail <= n) {
+      relocate_move(position + n, position, impl_.e_);
+      relocate_done(position + n, position, impl_.e_);
+      impl_.e_ += n;
+    } else {
+      if (folly::IsRelocatable<T>::value && usingStdAllocator::value) {
+        std::memmove(position + n, position, tail * sizeof(T));
+        impl_.e_ += n;
+      } else {
+        D_uninitialized_move_a(impl_.e_, impl_.e_ - n, impl_.e_);
+        try {
+          std::copy_backward(std::make_move_iterator(position),
+                             std::make_move_iterator(impl_.e_ - n), impl_.e_);
+        } catch (...) {
+          D_destroy_range_a(impl_.e_ - n, impl_.e_ + n);
+          impl_.e_ -= n;
+          throw;
+        }
+        impl_.e_ += n;
+        D_destroy_range_a(position, position + n);
+      }
+    }
+  }
+
+  void undo_window(iterator position, size_type n) noexcept {
+    D_destroy_range_a(position + n, impl_.e_);
+    impl_.e_ = position;
+  }
+
+  //---------------------------------------------------------------------------
+  // frame
+
+  void wrap_frame(T* ledge, size_type idx, size_type n) {
+    assert(size() >= idx);
+    assert(n != 0);
+
+    relocate_move(ledge, impl_.b_, impl_.b_ + idx);
+    try {
+      relocate_move(ledge + idx + n, impl_.b_ + idx, impl_.e_);
+    } catch (...) {
+      relocate_undo(ledge, impl_.b_, impl_.b_ + idx);
+      throw;
+    }
+    relocate_done(ledge, impl_.b_, impl_.b_ + idx);
+    relocate_done(ledge + idx + n, impl_.b_ + idx, impl_.e_);
+  }
+
+  //---------------------------------------------------------------------------
+  // use fresh?
+
+  bool insert_use_fresh(const_iterator cposition, size_type n) {
+    if (cposition == cend()) {
+      if (size() + n <= capacity()) return false;
+      if (reserve_in_place(size() + n)) return false;
+      return true;
+    }
+
+    if (size() + n > capacity()) return true;
+
+    return false;
+  }
+
+  //---------------------------------------------------------------------------
+  // interface
+
+  #define FOLLY_FBVECTOR_INSERT_START(cpos, n)                                \
+    assert(isValid(cpos));                                                    \
+    T* position = const_cast<T*>(cpos);                                       \
+    size_type idx = std::distance(impl_.b_, position);                        \
+    bool fresh = insert_use_fresh(position, n);                               \
+    T* b;                                                                     \
+    size_type newCap = 0;                                                     \
+                                                                              \
+    if (fresh) {                                                              \
+      newCap = computeInsertCapacity(n);                                      \
+      b = M_allocate(newCap);                                                 \
+    } else {                                                                  \
+      make_window(position, n);                                               \
+      b = impl_.b_;                                                           \
+    }                                                                         \
+                                                                              \
+    T* start = b + idx;                                                       \
+                                                                              \
+    try {                                                                     \
+
+    // construct the inserted elements
+
+  #define FOLLY_FBVECTOR_INSERT_TRY(cpos, n)                                  \
+    } catch (...) {                                                           \
+      if (fresh) {                                                            \
+        M_deallocate(b, newCap);                                              \
+      } else {                                                                \
+        undo_window(position, n);                                             \
+      }                                                                       \
+      throw;                                                                  \
+    }                                                                         \
+                                                                              \
+    if (fresh) {                                                              \
+      try {                                                                   \
+        wrap_frame(b, idx, n);                                                \
+      } catch (...) {                                                         \
+
+
+    // delete the inserted elements (exception has been thrown)
+
+  #define FOLLY_FBVECTOR_INSERT_END(cpos, n)                                  \
+        M_deallocate(b, newCap);                                              \
+        throw;                                                                \
+      }                                                                       \
+      if (impl_.b_) M_deallocate(impl_.b_, capacity());                       \
+      impl_.set(b, size() + n, newCap);                                       \
+      return impl_.b_ + idx;                                                  \
+    } else {                                                                  \
+      return position;                                                        \
+    }                                                                         \
+
+  //---------------------------------------------------------------------------
+  // insert functions
+public:
+
+  template <class... Args>
+  iterator emplace(const_iterator cpos, Args&&... args) {
+    FOLLY_FBVECTOR_INSERT_START(cpos, 1)
+      M_construct(start, std::forward<Args>(args)...);
+    FOLLY_FBVECTOR_INSERT_TRY(cpos, 1)
+      M_destroy(start);
+    FOLLY_FBVECTOR_INSERT_END(cpos, 1)
+  }
+
+  iterator insert(const_iterator cpos, const T& value) {
+    if (dataIsInternal(value)) return insert(cpos, T(value));
+
+    FOLLY_FBVECTOR_INSERT_START(cpos, 1)
+      M_construct(start, value);
+    FOLLY_FBVECTOR_INSERT_TRY(cpos, 1)
+      M_destroy(start);
+    FOLLY_FBVECTOR_INSERT_END(cpos, 1)
+  }
+
+  iterator insert(const_iterator cpos, T&& value) {
+    if (dataIsInternal(value)) return insert(cpos, T(std::move(value)));
+
+    FOLLY_FBVECTOR_INSERT_START(cpos, 1)
+      M_construct(start, std::move(value));
+    FOLLY_FBVECTOR_INSERT_TRY(cpos, 1)
+      M_destroy(start);
+    FOLLY_FBVECTOR_INSERT_END(cpos, 1)
+  }
+
+  iterator insert(const_iterator cpos, size_type n, VT value) {
+    if (n == 0) return (iterator)cpos;
+    if (dataIsInternalAndNotVT(value)) return insert(cpos, n, T(value));
+
+    FOLLY_FBVECTOR_INSERT_START(cpos, n)
+      D_uninitialized_fill_n_a(start, n, value);
+    FOLLY_FBVECTOR_INSERT_TRY(cpos, n)
+      D_destroy_range_a(start, start + n);
+    FOLLY_FBVECTOR_INSERT_END(cpos, n)
+  }
+
+  template <class It, class Category = typename
+            std::iterator_traits<It>::iterator_category>
+  iterator insert(const_iterator cpos, It first, It last) {
+    return insert(cpos, first, last, Category());
+  }
+
+  iterator insert(const_iterator cpos, std::initializer_list<T> il) {
+    return insert(cpos, il.begin(), il.end());
+  }
+
+  //---------------------------------------------------------------------------
+  // insert dispatch for iterator types
+private:
+
+  template <class FIt>
+  iterator insert(const_iterator cpos, FIt first, FIt last,
+                  std::forward_iterator_tag) {
+    size_type n = std::distance(first, last);
+    if (n == 0) return (iterator)cpos;
+
+    FOLLY_FBVECTOR_INSERT_START(cpos, n)
+      D_uninitialized_copy_a(start, first, last);
+    FOLLY_FBVECTOR_INSERT_TRY(cpos, n)
+      D_destroy_range_a(start, start + n);
+    FOLLY_FBVECTOR_INSERT_END(cpos, n)
+  }
+
+  template <class IIt>
+  iterator insert(const_iterator cpos, IIt first, IIt last,
+                  std::input_iterator_tag) {
+    T* position = const_cast<T*>(cpos);
+    assert(isValid(position));
+    size_type idx = std::distance(begin(), position);
+
+    fbvector storage(std::make_move_iterator(position),
+                     std::make_move_iterator(end()),
+                     A::select_on_container_copy_construction(impl_));
+    M_destroy_range_e(position);
+    for (; first != last; ++first) emplace_back(*first);
+    insert(cend(), std::make_move_iterator(storage.begin()),
+           std::make_move_iterator(storage.end()));
+    return impl_.b_ + idx;
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // lexicographical functions (others from boost::totally_ordered superclass)
+public:
+
+  bool operator==(const fbvector& other) const {
+    return size() == other.size() && std::equal(begin(), end(), other.begin());
+  }
+
+  bool operator<(const fbvector& other) const {
+    return std::lexicographical_compare(
+      begin(), end(), other.begin(), other.end());
+  }
+
+  //===========================================================================
+  //---------------------------------------------------------------------------
+  // friends
+private:
+
+  template <class _T, class _A>
+  friend _T* relinquish(fbvector<_T, _A>&);
+
+  template <class _T, class _A>
+  friend void attach(fbvector<_T, _A>&, _T* data, size_t sz, size_t cap);
+
+}; // class fbvector
+
+
+//=============================================================================
+//-----------------------------------------------------------------------------
+// outlined functions (gcc, you finicky compiler you)
+
+template <typename T, typename Allocator>
+template <class... Args>
+void fbvector<T, Allocator>::emplace_back_aux(Args&&... args) {
+  size_type byte_sz = folly::goodMallocSize(
+    computePushBackCapacity() * sizeof(T));
+  if (usingStdAllocator::value
+      && usingJEMalloc()
+      && ((impl_.z_ - impl_.b_) * sizeof(T) >=
+          folly::jemallocMinInPlaceExpandable)) {
+    // Try to reserve in place.
+    // Ask xallocx to allocate in place at least size()+1 and at most sz space.
+    // xallocx will allocate as much as possible within that range, which
+    //  is the best possible outcome: if sz space is available, take it all,
+    //  otherwise take as much as possible. If nothing is available, then fail.
+    // In this fashion, we never relocate if there is a possibility of
+    //  expanding in place, and we never reallocate by less than the desired
+    //  amount unless we cannot expand further. Hence we will not reallocate
+    //  sub-optimally twice in a row (modulo the blocking memory being freed).
+    size_type lower = folly::goodMallocSize(sizeof(T) + size() * sizeof(T));
+    size_type upper = byte_sz;
+    size_type extra = upper - lower;
+
+    void* p = impl_.b_;
+    size_t actual;
+
+    if ((actual = xallocx(p, lower, extra, 0)) >= lower) {
+      impl_.z_ = impl_.b_ + actual / sizeof(T);
+      M_construct(impl_.e_, std::forward<Args>(args)...);
+      ++impl_.e_;
+      return;
+    }
+  }
+
+  // Reallocation failed. Perform a manual relocation.
+  size_type sz = byte_sz / sizeof(T);
+  auto newB = M_allocate(sz);
+  auto newE = newB + size();
+  try {
+    if (folly::IsRelocatable<T>::value && usingStdAllocator::value) {
+      // For linear memory access, relocate before construction.
+      // By the test condition, relocate is noexcept.
+      // Note that there is no cleanup to do if M_construct throws - that's
+      //  one of the beauties of relocation.
+      // Benchmarks for this code have high variance, and seem to be close.
+      relocate_move(newB, impl_.b_, impl_.e_);
+      M_construct(newE, std::forward<Args>(args)...);
+      ++newE;
+    } else {
+      M_construct(newE, std::forward<Args>(args)...);
+      ++newE;
+      try {
+        M_relocate(newB);
+      } catch (...) {
+        M_destroy(newE - 1);
+        throw;
+      }
+    }
+  } catch (...) {
+    M_deallocate(newB, sz);
+    throw;
+  }
+  if (impl_.b_) M_deallocate(impl_.b_, size());
+  impl_.b_ = newB;
+  impl_.e_ = newE;
+  impl_.z_ = newB + sz;
+}
+
+//=============================================================================
+//-----------------------------------------------------------------------------
+// specialized functions
+
+template <class T, class A>
+void swap(fbvector<T, A>& lhs, fbvector<T, A>& rhs) noexcept {
+  lhs.swap(rhs);
+}
+
+//=============================================================================
+//-----------------------------------------------------------------------------
+// other
+
+namespace detail {
+
+// Format support.
+template <class T, class A>
+struct IndexableTraits<fbvector<T, A>>
+  : public IndexableTraitsSeq<fbvector<T, A>> {
+};
+
+}  // namespace detail
+
+template <class T, class A>
+void compactResize(fbvector<T, A>* v, size_t sz) {
+  v->resize(sz);
+  v->shrink_to_fit();
+}
+
+// DANGER
+//
+// relinquish and attach are not a members function specifically so that it is
+//  awkward to call them. It is very easy to shoot yourself in the foot with
+//  these functions.
+//
+// If you call relinquish, then it is your responsibility to free the data
+//  and the storage, both of which may have been generated in a non-standard
+//  way through the fbvector's allocator.
+//
+// If you call attach, it is your responsibility to ensure that the fbvector
+//  is fresh (size and capacity both zero), and that the supplied data is
+//  capable of being manipulated by the allocator.
+// It is acceptable to supply a stack pointer IF:
+//  (1) The vector's data does not outlive the stack pointer. This includes
+//      extension of the data's life through a move operation.
+//  (2) The pointer has enough capacity that the vector will never be
+//      relocated.
+//  (3) Insert is not called on the vector; these functions have leeway to
+//      relocate the vector even if there is enough capacity.
+//  (4) A stack pointer is compatible with the fbvector's allocator.
+//
+
+template <class T, class A>
+T* relinquish(fbvector<T, A>& v) {
+  T* ret = v.data();
+  v.impl_.b_ = v.impl_.e_ = v.impl_.z_ = nullptr;
+  return ret;
+}
+
+template <class T, class A>
+void attach(fbvector<T, A>& v, T* data, size_t sz, size_t cap) {
+  assert(v.data() == nullptr);
+  v.impl_.b_ = data;
+  v.impl_.e_ = data + sz;
+  v.impl_.z_ = data + cap;
+}
+
+} // namespace folly
+
+#endif // FOLLY_FBVECTOR_H
diff --git a/faux-folly/folly/File.cpp b/faux-folly/folly/File.cpp
new file mode 100644
index 0000000..3c53573
--- /dev/null
+++ b/faux-folly/folly/File.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2015 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/File.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+#include <folly/Exception.h>
+#include <folly/FileUtil.h>
+#include <folly/Format.h>
+#include <folly/ScopeGuard.h>
+
+#include <system_error>
+
+#include <glog/logging.h>
+
+namespace folly {
+
+File::File()
+  : fd_(-1)
+  , ownsFd_(false)
+{}
+
+File::File(int fd, bool ownsFd)
+  : fd_(fd)
+  , ownsFd_(ownsFd) {
+  CHECK_GE(fd, -1) << "fd must be -1 or non-negative";
+  CHECK(fd != -1 || !ownsFd) << "cannot own -1";
+}
+
+File::File(const char* name, int flags, mode_t mode)
+  : fd_(::open(name, flags, mode))
+  , ownsFd_(false) {
+  if (fd_ == -1) {
+    throwSystemError(folly::format("open(\"{}\", {:#o}, 0{:#o}) failed",
+                                   name, flags, mode).fbstr());
+  }
+  ownsFd_ = true;
+}
+
+File::File(const std::string& name, int flags, mode_t mode)
+  : File(name.c_str(), flags, mode) {}
+
+File::File(StringPiece name, int flags, mode_t mode)
+  : File(name.str(), flags, mode) {}
+
+File::File(File&& other) noexcept
+  : fd_(other.fd_)
+  , ownsFd_(other.ownsFd_) {
+  other.release();
+}
+
+File& File::operator=(File&& other) {
+  closeNoThrow();
+  swap(other);
+  return *this;
+}
+
+File::~File() {
+  auto fd = fd_;
+  if (!closeNoThrow()) {  // ignore most errors
+    DCHECK_NE(errno, EBADF) << "closing fd " << fd << ", it may already "
+      << "have been closed. Another time, this might close the wrong FD.";
+  }
+}
+
+/* static */ File File::temporary() {
+  // make a temp file with tmpfile(), dup the fd, then return it in a File.
+  FILE* tmpFile = tmpfile();
+  checkFopenError(tmpFile, "tmpfile() failed");
+  SCOPE_EXIT { fclose(tmpFile); };
+
+  int fd = ::dup(fileno(tmpFile));
+  checkUnixError(fd, "dup() failed");
+
+  return File(fd, true);
+}
+
+int File::release() noexcept {
+  int released = fd_;
+  fd_ = -1;
+  ownsFd_ = false;
+  return released;
+}
+
+void File::swap(File& other) {
+  using std::swap;
+  swap(fd_, other.fd_);
+  swap(ownsFd_, other.ownsFd_);
+}
+
+void swap(File& a, File& b) {
+  a.swap(b);
+}
+
+File File::dup() const {
+  if (fd_ != -1) {
+    int fd = ::dup(fd_);
+    checkUnixError(fd, "dup() failed");
+
+    return File(fd, true);
+  }
+
+  return File();
+}
+
+void File::close() {
+  if (!closeNoThrow()) {
+    throwSystemError("close() failed");
+  }
+}
+
+bool File::closeNoThrow() {
+  int r = ownsFd_ ? ::close(fd_) : 0;
+  release();
+  return r == 0;
+}
+
+void File::lock() { doLock(LOCK_EX); }
+bool File::try_lock() { return doTryLock(LOCK_EX); }
+void File::lock_shared() { doLock(LOCK_SH); }
+bool File::try_lock_shared() { return doTryLock(LOCK_SH); }
+
+void File::doLock(int op) {
+  checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)");
+}
+
+bool File::doTryLock(int op) {
+  int r = flockNoInt(fd_, op | LOCK_NB);
+  // flock returns EWOULDBLOCK if already locked
+  if (r == -1 && errno == EWOULDBLOCK) return false;
+  checkUnixError(r, "flock() failed (try_lock)");
+  return true;
+}
+
+void File::unlock() {
+  checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)");
+}
+void File::unlock_shared() { unlock(); }
+
+}  // namespace folly
diff --git a/faux-folly/folly/File.h b/faux-folly/folly/File.h
new file mode 100644
index 0000000..4592514
--- /dev/null
+++ b/faux-folly/folly/File.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_FILE_H_
+#define FOLLY_FILE_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <folly/Portability.h>
+#include <folly/Range.h>
+
+namespace folly {
+
+/**
+ * A File represents an open file.
+ */
+class File {
+ public:
+  /**
+   * Creates an empty File object, for late initialization.
+   */
+  File();
+
+  /**
+   * Create a File object from an existing file descriptor.
+   * Takes ownership of the file descriptor if ownsFd is true.
+   */
+  explicit File(int fd, bool ownsFd = false);
+
+  /**
+   * Open and create a file object.  Throws on error.
+   */
+  explicit File(const char* name, int flags = O_RDONLY, mode_t mode = 0666);
+  explicit File(
+      const std::string& name, int flags = O_RDONLY, mode_t mode = 0666);
+  explicit File(StringPiece name, int flags = O_RDONLY, mode_t mode = 0666);
+
+  ~File();
+
+  /**
+   * Create and return a temporary, owned file (uses tmpfile()).
+   */
+  static File temporary();
+
+  /**
+   * Return the file descriptor, or -1 if the file was closed.
+   */
+  int fd() const { return fd_; }
+
+  /**
+   * Returns 'true' iff the file was successfully opened.
+   */
+  explicit operator bool() const {
+    return fd_ != -1;
+  }
+
+  /**
+   * Duplicate file descriptor and return File that owns it.
+   */
+  File dup() const;
+
+  /**
+   * If we own the file descriptor, close the file and throw on error.
+   * Otherwise, do nothing.
+   */
+  void close();
+
+  /**
+   * Closes the file (if owned).  Returns true on success, false (and sets
+   * errno) on error.
+   */
+  bool closeNoThrow();
+
+  /**
+   * Returns and releases the file descriptor; no longer owned by this File.
+   * Returns -1 if the File object didn't wrap a file.
+   */
+  int release() noexcept;
+
+  /**
+   * Swap this File with another.
+   */
+  void swap(File& other);
+
+  // movable
+  File(File&&) noexcept;
+  File& operator=(File&&);
+
+  // FLOCK (INTERPROCESS) LOCKS
+  //
+  // NOTE THAT THESE LOCKS ARE flock() LOCKS.  That is, they may only be used
+  // for inter-process synchronization -- an attempt to acquire a second lock
+  // on the same file descriptor from the same process may succeed.  Attempting
+  // to acquire a second lock on a different file descriptor for the same file
+  // should fail, but some systems might implement flock() using fcntl() locks,
+  // in which case it will succeed.
+  void lock();
+  bool try_lock();
+  void unlock();
+
+  void lock_shared();
+  bool try_lock_shared();
+  void unlock_shared();
+
+ private:
+  void doLock(int op);
+  bool doTryLock(int op);
+
+  // unique
+  File(const File&) = delete;
+  File& operator=(const File&) = delete;
+
+  int fd_;
+  bool ownsFd_;
+};
+
+void swap(File& a, File& b);
+
+
+}  // namespace folly
+
+#endif /* FOLLY_FILE_H_ */
diff --git a/faux-folly/folly/FileUtil.cpp b/faux-folly/folly/FileUtil.cpp
new file mode 100644
index 0000000..728f1d2
--- /dev/null
+++ b/faux-folly/folly/FileUtil.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2015 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/FileUtil.h>
+
+#include <cerrno>
+#ifdef __APPLE__
+#include <fcntl.h>
+#endif
+#include <sys/file.h>
+#include <sys/socket.h>
+
+#include <folly/detail/FileUtilDetail.h>
+
+namespace folly {
+
+using namespace fileutil_detail;
+
+int openNoInt(const char* name, int flags, mode_t mode) {
+  return wrapNoInt(open, name, flags, mode);
+}
+
+int closeNoInt(int fd) {
+  int r = close(fd);
+  // Ignore EINTR.  On Linux, close() may only return EINTR after the file
+  // descriptor has been closed, so you must not retry close() on EINTR --
+  // in the best case, you'll get EBADF, and in the worst case, you'll end up
+  // closing a different file (one opened from another thread).
+  //
+  // Interestingly enough, the Single Unix Specification says that the state
+  // of the file descriptor is unspecified if close returns EINTR.  In that
+  // case, the safe thing to do is also not to retry close() -- leaking a file
+  // descriptor is definitely better than closing the wrong file.
+  if (r == -1 && errno == EINTR) {
+    r = 0;
+  }
+  return r;
+}
+
+int fsyncNoInt(int fd) {
+  return wrapNoInt(fsync, fd);
+}
+
+int dupNoInt(int fd) {
+  return wrapNoInt(dup, fd);
+}
+
+int dup2NoInt(int oldfd, int newfd) {
+  return wrapNoInt(dup2, oldfd, newfd);
+}
+
+int fdatasyncNoInt(int fd) {
+#if defined(__APPLE__)
+  return wrapNoInt(fcntl, fd, F_FULLFSYNC);
+#elif defined(__FreeBSD__) || defined(_MSC_VER)
+  return wrapNoInt(fsync, fd);
+#else
+  return wrapNoInt(fdatasync, fd);
+#endif
+}
+
+int ftruncateNoInt(int fd, off_t len) {
+  return wrapNoInt(ftruncate, fd, len);
+}
+
+int truncateNoInt(const char* path, off_t len) {
+  return wrapNoInt(truncate, path, len);
+}
+
+int flockNoInt(int fd, int operation) {
+  return wrapNoInt(flock, fd, operation);
+}
+
+int shutdownNoInt(int fd, int how) {
+  return wrapNoInt(shutdown, fd, how);
+}
+
+ssize_t readNoInt(int fd, void* buf, size_t count) {
+  return wrapNoInt(read, fd, buf, count);
+}
+
+ssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset) {
+  return wrapNoInt(pread, fd, buf, count, offset);
+}
+
+ssize_t readvNoInt(int fd, const iovec* iov, int count) {
+  return wrapNoInt(writev, fd, iov, count);
+}
+
+ssize_t writeNoInt(int fd, const void* buf, size_t count) {
+  return wrapNoInt(write, fd, buf, count);
+}
+
+ssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset) {
+  return wrapNoInt(pwrite, fd, buf, count, offset);
+}
+
+ssize_t writevNoInt(int fd, const iovec* iov, int count) {
+  return wrapNoInt(writev, fd, iov, count);
+}
+
+ssize_t readFull(int fd, void* buf, size_t count) {
+  return wrapFull(read, fd, buf, count);
+}
+
+ssize_t preadFull(int fd, void* buf, size_t count, off_t offset) {
+  return wrapFull(pread, fd, buf, count, offset);
+}
+
+ssize_t writeFull(int fd, const void* buf, size_t count) {
+  return wrapFull(write, fd, const_cast<void*>(buf), count);
+}
+
+ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) {
+  return wrapFull(pwrite, fd, const_cast<void*>(buf), count, offset);
+}
+
+ssize_t readvFull(int fd, iovec* iov, int count) {
+  return wrapvFull(readv, fd, iov, count);
+}
+
+#if FOLLY_HAVE_PREADV
+ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) {
+  return wrapvFull(preadv, fd, iov, count, offset);
+}
+#endif
+
+ssize_t writevFull(int fd, iovec* iov, int count) {
+  return wrapvFull(writev, fd, iov, count);
+}
+
+#if FOLLY_HAVE_PWRITEV
+ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) {
+  return wrapvFull(pwritev, fd, iov, count, offset);
+}
+#endif
+
+}  // namespaces
diff --git a/faux-folly/folly/FileUtil.h b/faux-folly/folly/FileUtil.h
new file mode 100644
index 0000000..5adcd62
--- /dev/null
+++ b/faux-folly/folly/FileUtil.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_FILEUTIL_H_
+#define FOLLY_FILEUTIL_H_
+
+#include <folly/Conv.h>
+#include <folly/Portability.h>
+#include <folly/ScopeGuard.h>
+
+#include <cassert>
+#include <limits>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+namespace folly {
+
+/**
+ * Convenience wrappers around some commonly used system calls.  The *NoInt
+ * wrappers retry on EINTR.  The *Full wrappers retry on EINTR and also loop
+ * until all data is written.  Note that *Full wrappers weaken the thread
+ * semantics of underlying system calls.
+ */
+int openNoInt(const char* name, int flags, mode_t mode = 0666);
+int closeNoInt(int fd);
+int dupNoInt(int fd);
+int dup2NoInt(int oldfd, int newfd);
+int fsyncNoInt(int fd);
+int fdatasyncNoInt(int fd);
+int ftruncateNoInt(int fd, off_t len);
+int truncateNoInt(const char* path, off_t len);
+int flockNoInt(int fd, int operation);
+int shutdownNoInt(int fd, int how);
+
+ssize_t readNoInt(int fd, void* buf, size_t n);
+ssize_t preadNoInt(int fd, void* buf, size_t n, off_t offset);
+ssize_t readvNoInt(int fd, const iovec* iov, int count);
+
+ssize_t writeNoInt(int fd, const void* buf, size_t n);
+ssize_t pwriteNoInt(int fd, const void* buf, size_t n, off_t offset);
+ssize_t writevNoInt(int fd, const iovec* iov, int count);
+
+/**
+ * Wrapper around read() (and pread()) that, in addition to retrying on
+ * EINTR, will loop until all data is read.
+ *
+ * This wrapper is only useful for blocking file descriptors (for non-blocking
+ * file descriptors, you have to be prepared to deal with incomplete reads
+ * anyway), and only exists because POSIX allows read() to return an incomplete
+ * read if interrupted by a signal (instead of returning -1 and setting errno
+ * to EINTR).
+ *
+ * Note that this wrapper weakens the thread safety of read(): the file pointer
+ * is shared between threads, but the system call is atomic.  If multiple
+ * threads are reading from a file at the same time, you don't know where your
+ * data came from in the file, but you do know that the returned bytes were
+ * contiguous.  You can no longer make this assumption if using readFull().
+ * You should probably use pread() when reading from the same file descriptor
+ * from multiple threads simultaneously, anyway.
+ *
+ * Note that readvFull and preadvFull require iov to be non-const, unlike
+ * readv and preadv.  The contents of iov after these functions return
+ * is unspecified.
+ */
+ssize_t readFull(int fd, void* buf, size_t n);
+ssize_t preadFull(int fd, void* buf, size_t n, off_t offset);
+ssize_t readvFull(int fd, iovec* iov, int count);
+#if FOLLY_HAVE_PREADV
+ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset);
+#endif
+
+/**
+ * Similar to readFull and preadFull above, wrappers around write() and
+ * pwrite() that loop until all data is written.
+ *
+ * Generally, the write() / pwrite() system call may always write fewer bytes
+ * than requested, just like read().  In certain cases (such as when writing to
+ * a pipe), POSIX provides stronger guarantees, but not in the general case.
+ * For example, Linux (even on a 64-bit platform) won't write more than 2GB in
+ * one write() system call.
+ *
+ * Note that writevFull and pwritevFull require iov to be non-const, unlike
+ * writev and pwritev.  The contents of iov after these functions return
+ * is unspecified.
+ */
+ssize_t writeFull(int fd, const void* buf, size_t n);
+ssize_t pwriteFull(int fd, const void* buf, size_t n, off_t offset);
+ssize_t writevFull(int fd, iovec* iov, int count);
+#if FOLLY_HAVE_PWRITEV
+ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset);
+#endif
+
+/**
+ * Read entire file (if num_bytes is defaulted) or no more than
+ * num_bytes (otherwise) into container *out. The container is assumed
+ * to be contiguous, with element size equal to 1, and offer size(),
+ * reserve(), and random access (e.g. std::vector<char>, std::string,
+ * fbstring).
+ *
+ * Returns: true on success or false on failure. In the latter case
+ * errno will be set appropriately by the failing system primitive.
+ */
+template <class Container>
+bool readFile(const char* file_name, Container& out,
+              size_t num_bytes = std::numeric_limits<size_t>::max()) {
+  static_assert(sizeof(out[0]) == 1,
+                "readFile: only containers with byte-sized elements accepted");
+  assert(file_name);
+
+  const auto fd = openNoInt(file_name, O_RDONLY);
+  if (fd == -1) return false;
+
+  size_t soFar = 0; // amount of bytes successfully read
+  SCOPE_EXIT {
+    assert(out.size() >= soFar); // resize better doesn't throw
+    out.resize(soFar);
+    // Ignore errors when closing the file
+    closeNoInt(fd);
+  };
+
+  // Obtain file size:
+  struct stat buf;
+  if (fstat(fd, &buf) == -1) return false;
+  // Some files (notably under /proc and /sys on Linux) lie about
+  // their size, so treat the size advertised by fstat under advise
+  // but don't rely on it. In particular, if the size is zero, we
+  // should attempt to read stuff. If not zero, we'll attempt to read
+  // one extra byte.
+  constexpr size_t initialAlloc = 1024 * 4;
+  out.resize(
+    std::min(
+      buf.st_size > 0 ? folly::to<size_t>(buf.st_size + 1) : initialAlloc,
+      num_bytes));
+
+  while (soFar < out.size()) {
+    const auto actual = readFull(fd, &out[soFar], out.size() - soFar);
+    if (actual == -1) {
+      return false;
+    }
+    soFar += actual;
+    if (soFar < out.size()) {
+      // File exhausted
+      break;
+    }
+    // Ew, allocate more memory. Use exponential growth to avoid
+    // quadratic behavior. Cap size to num_bytes.
+    out.resize(std::min(out.size() * 3 / 2, num_bytes));
+  }
+
+  return true;
+}
+
+/**
+ * Writes container to file. The container is assumed to be
+ * contiguous, with element size equal to 1, and offering STL-like
+ * methods empty(), size(), and indexed access
+ * (e.g. std::vector<char>, std::string, fbstring, StringPiece).
+ *
+ * "flags" dictates the open flags to use. Default is to create file
+ * if it doesn't exist and truncate it.
+ *
+ * Returns: true on success or false on failure. In the latter case
+ * errno will be set appropriately by the failing system primitive.
+ */
+template <class Container>
+bool writeFile(const Container& data, const char* filename,
+              int flags = O_WRONLY | O_CREAT | O_TRUNC) {
+  static_assert(sizeof(data[0]) == 1,
+                "writeFile works with element size equal to 1");
+  int fd = open(filename, flags, 0666);
+  if (fd == -1) {
+    return false;
+  }
+  bool ok = data.empty() ||
+    writeFull(fd, &data[0], data.size()) == static_cast<ssize_t>(data.size());
+  return closeNoInt(fd) == 0 && ok;
+}
+
+}  // namespaces
+
+#endif /* FOLLY_FILEUTIL_H_ */
diff --git a/faux-folly/folly/Foreach.h b/faux-folly/folly/Foreach.h
new file mode 100644
index 0000000..9d67c14
--- /dev/null
+++ b/faux-folly/folly/Foreach.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BASE_FOREACH_H_
+#define FOLLY_BASE_FOREACH_H_
+
+/*
+ * Iterim macros (until we have C++0x range-based for) that simplify
+ * writing loops of the form
+ *
+ * for (Container<data>::iterator i = c.begin(); i != c.end(); ++i) statement
+ *
+ * Just replace the above with:
+ *
+ * FOR_EACH (i, c) statement
+ *
+ * and everything is taken care of.
+ *
+ * The implementation is a bit convoluted to make sure the container is
+ * only evaluated once (however, keep in mind that c.end() is evaluated
+ * at every pass through the loop). To ensure the container is not
+ * evaluated multiple times, the macro defines one do-nothing if
+ * statement to inject the Boolean variable FOR_EACH_state1, and then a
+ * for statement that is executed only once, which defines the variable
+ * FOR_EACH_state2 holding a rvalue reference to the container being
+ * iterated. The workhorse is the last loop, which uses the just defined
+ * rvalue reference FOR_EACH_state2.
+ *
+ * The state variables are nested so they don't interfere; you can use
+ * FOR_EACH multiple times in the same scope, either at the same level or
+ * nested.
+ *
+ * In optimized builds g++ eliminates the extra gymnastics entirely and
+ * generates code 100% identical to the handwritten loop.
+ */
+
+#include <type_traits>
+
+/*
+ * Shorthand for:
+ *   for (auto i = c.begin(); i != c.end(); ++i)
+ * except that c is only evaluated once.
+ */
+#define FOR_EACH(i, c)                              \
+  if (bool FOR_EACH_state1 = false) {} else         \
+    for (auto && FOR_EACH_state2 = (c);             \
+         !FOR_EACH_state1; FOR_EACH_state1 = true)  \
+      for (auto i = FOR_EACH_state2.begin();        \
+           i != FOR_EACH_state2.end(); ++i)
+
+/*
+ * Similar to FOR_EACH, but iterates the container backwards by
+ * using rbegin() and rend().
+ */
+#define FOR_EACH_R(i, c)                                \
+  if (bool FOR_EACH_R_state1 = false) {} else           \
+    for (auto && FOR_EACH_R_state2 = (c);               \
+         !FOR_EACH_R_state1; FOR_EACH_R_state1 = true)  \
+      for (auto i = FOR_EACH_R_state2.rbegin();         \
+           i != FOR_EACH_R_state2.rend(); ++i)
+
+/*
+ * Similar to FOR_EACH but also allows client to specify a 'count' variable
+ * to track the current iteration in the loop (starting at zero).
+ * Similar to python's enumerate() function.  For example:
+ * string commaSeparatedValues = "VALUES: ";
+ * FOR_EACH_ENUMERATE(ii, value, columns) {   // don't want comma at the end!
+ *   commaSeparatedValues += (ii == 0) ? *value : string(",") + *value;
+ * }
+ */
+#define FOR_EACH_ENUMERATE(count, i, c)                                \
+  if (bool FOR_EACH_state1 = false) {} else                            \
+    for (auto && FOR_EACH_state2 = (c);                                \
+         !FOR_EACH_state1; FOR_EACH_state1 = true)                     \
+      if (size_t FOR_EACH_privateCount = 0) {} else                    \
+        if (const size_t& count = FOR_EACH_privateCount) {} else       \
+          for (auto i = FOR_EACH_state2.begin();                       \
+               i != FOR_EACH_state2.end(); ++FOR_EACH_privateCount, ++i)
+
+/**
+ * Similar to FOR_EACH, but gives the user the key and value for each entry in
+ * the container, instead of just the iterator to the entry. For example:
+ *   map<string, string> testMap;
+ *   FOR_EACH_KV(key, value, testMap) {
+ *      cout << key << " " << value;
+ *   }
+ */
+#define FOR_EACH_KV(k, v, c)                                    \
+  if (unsigned int FOR_EACH_state1 = 0) {} else                 \
+    for (auto && FOR_EACH_state2 = (c);                         \
+         !FOR_EACH_state1; FOR_EACH_state1 = 1)                 \
+      for (auto FOR_EACH_state3 = FOR_EACH_state2.begin();      \
+           FOR_EACH_state3 != FOR_EACH_state2.end();            \
+           FOR_EACH_state1 == 2                                 \
+             ? ((FOR_EACH_state1 = 0), ++FOR_EACH_state3)       \
+             : (FOR_EACH_state3 = FOR_EACH_state2.end()))       \
+        for (auto &k = FOR_EACH_state3->first;                  \
+             !FOR_EACH_state1; ++FOR_EACH_state1)               \
+          for (auto &v = FOR_EACH_state3->second;               \
+               !FOR_EACH_state1; ++FOR_EACH_state1)
+
+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>(0)) == 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;
+}
+
+} }
+
+/*
+ * Iteration with given limits. end is assumed to be reachable from
+ * begin. end is evaluated every pass through the loop.
+ *
+ * NOTE: The type of the loop variable should be the common type of "begin"
+ *       and "end". e.g. If "begin" is "int" but "end" is "long", we want "i"
+ *       to be "long". This is done by getting the type of (true ? begin : end)
+ */
+#define FOR_EACH_RANGE(i, begin, end)           \
+  for (auto i = (true ? (begin) : (end));       \
+       ::folly::detail::notThereYet(i, (end));  \
+       ++i)
+
+/*
+ * Iteration with given limits. begin is assumed to be reachable from
+ * end by successive decrements. begin is evaluated every pass through
+ * the loop.
+ *
+ * NOTE: The type of the loop variable should be the common type of "begin"
+ *       and "end". e.g. If "begin" is "int" but "end" is "long", we want "i"
+ *       to be "long". This is done by getting the type of (false ? begin : end)
+ */
+#define FOR_EACH_RANGE_R(i, begin, end) \
+  for (auto i = (false ? (begin) : (end)); ::folly::detail::downTo(i, (begin));)
+
+#endif
diff --git a/faux-folly/folly/Format-inl.h b/faux-folly/folly/Format-inl.h
new file mode 100644
index 0000000..64591d6
--- /dev/null
+++ b/faux-folly/folly/Format-inl.h
@@ -0,0 +1,1102 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_FORMAT_H_
+#error This file may only be included from Format.h.
+#endif
+
+#include <array>
+#include <deque>
+#include <map>
+#include <unordered_map>
+#include <vector>
+
+#include <folly/Exception.h>
+#include <folly/FormatTraits.h>
+#include <folly/Traits.h>
+
+// Ignore -Wformat-nonliteral warnings within this file
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+namespace folly {
+
+namespace detail {
+
+// Updates the end of the buffer after the comma separators have been added.
+void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer);
+
+extern const char formatHexUpper[256][2];
+extern const char formatHexLower[256][2];
+extern const char formatOctal[512][3];
+extern const char formatBinary[256][8];
+
+const size_t kMaxHexLength = 2 * sizeof(uintmax_t);
+const size_t kMaxOctalLength = 3 * sizeof(uintmax_t);
+const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t);
+
+/**
+ * Convert an unsigned to hex, using repr (which maps from each possible
+ * 2-hex-bytes value to the 2-character representation).
+ *
+ * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of
+ * the supplied buffer and returns the offset of the beginning of the string
+ * from the start of the buffer.  The formatted string will be in range
+ * [buf+begin, buf+bufLen).
+ */
+template <class Uint>
+size_t uintToHex(char* buffer, size_t bufLen, Uint v,
+                 const char (&repr)[256][2]) {
+  // 'v >>= 7, v >>= 1' is no more than a work around to get rid of shift size
+  // warning when Uint = uint8_t (it's false as v >= 256 implies sizeof(v) > 1).
+  for (; !less_than<unsigned, 256>(v); v >>= 7, v >>= 1) {
+    auto b = v & 0xff;
+    bufLen -= 2;
+    buffer[bufLen] = repr[b][0];
+    buffer[bufLen + 1] = repr[b][1];
+  }
+  buffer[--bufLen] = repr[v][1];
+  if (v >= 16) {
+    buffer[--bufLen] = repr[v][0];
+  }
+  return bufLen;
+}
+
+/**
+ * Convert an unsigned to hex, using lower-case letters for the digits
+ * above 9.  See the comments for uintToHex.
+ */
+template <class Uint>
+inline size_t uintToHexLower(char* buffer, size_t bufLen, Uint v) {
+  return uintToHex(buffer, bufLen, v, formatHexLower);
+}
+
+/**
+ * Convert an unsigned to hex, using upper-case letters for the digits
+ * above 9.  See the comments for uintToHex.
+ */
+template <class Uint>
+inline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) {
+  return uintToHex(buffer, bufLen, v, formatHexUpper);
+}
+
+/**
+ * Convert an unsigned to octal.
+ *
+ * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of
+ * the supplied buffer and returns the offset of the beginning of the string
+ * from the start of the buffer.  The formatted string will be in range
+ * [buf+begin, buf+bufLen).
+ */
+template <class Uint>
+size_t uintToOctal(char* buffer, size_t bufLen, Uint v) {
+  auto& repr = formatOctal;
+  // 'v >>= 7, v >>= 2' is no more than a work around to get rid of shift size
+  // warning when Uint = uint8_t (it's false as v >= 512 implies sizeof(v) > 1).
+  for (; !less_than<unsigned, 512>(v); v >>= 7, v >>= 2) {
+    auto b = v & 0x1ff;
+    bufLen -= 3;
+    buffer[bufLen] = repr[b][0];
+    buffer[bufLen + 1] = repr[b][1];
+    buffer[bufLen + 2] = repr[b][2];
+  }
+  buffer[--bufLen] = repr[v][2];
+  if (v >= 8) {
+    buffer[--bufLen] = repr[v][1];
+  }
+  if (v >= 64) {
+    buffer[--bufLen] = repr[v][0];
+  }
+  return bufLen;
+}
+
+/**
+ * Convert an unsigned to binary.
+ *
+ * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of
+ * the supplied buffer and returns the offset of the beginning of the string
+ * from the start of the buffer.  The formatted string will be in range
+ * [buf+begin, buf+bufLen).
+ */
+template <class Uint>
+size_t uintToBinary(char* buffer, size_t bufLen, Uint v) {
+  auto& repr = formatBinary;
+  if (v == 0) {
+    buffer[--bufLen] = '0';
+    return bufLen;
+  }
+  for (; v; v >>= 7, v >>= 1) {
+    auto b = v & 0xff;
+    bufLen -= 8;
+    memcpy(buffer + bufLen, &(repr[b][0]), 8);
+  }
+  while (buffer[bufLen] == '0') {
+    ++bufLen;
+  }
+  return bufLen;
+}
+
+}  // namespace detail
+
+template <class Derived, bool containerMode, class... Args>
+BaseFormatter<Derived, containerMode, Args...>::BaseFormatter(StringPiece str,
+                                                              Args&&... args)
+    : str_(str),
+      values_(FormatValue<typename std::decay<Args>::type>(
+          std::forward<Args>(args))...) {
+  static_assert(!containerMode || sizeof...(Args) == 1,
+                "Exactly one argument required in container mode");
+}
+
+template <class Derived, bool containerMode, class... Args>
+template <class Output>
+void BaseFormatter<Derived, containerMode, Args...>::operator()(Output& out)
+    const {
+  // Copy raw string (without format specifiers) to output;
+  // not as simple as we'd like, as we still need to translate "}}" to "}"
+  // and throw if we see any lone "}"
+  auto outputString = [&out] (StringPiece s) {
+    auto p = s.begin();
+    auto end = s.end();
+    while (p != end) {
+      auto q = static_cast<const char*>(memchr(p, '}', end - p));
+      if (!q) {
+        out(StringPiece(p, end));
+        break;
+      }
+      ++q;
+      out(StringPiece(p, q));
+      p = q;
+
+      if (p == end || *p != '}') {
+        throw BadFormatArg("folly::format: single '}' in format string");
+      }
+      ++p;
+    }
+  };
+
+  auto p = str_.begin();
+  auto end = str_.end();
+
+  int nextArg = 0;
+  bool hasDefaultArgIndex = false;
+  bool hasExplicitArgIndex = false;
+  while (p != end) {
+    auto q = static_cast<const char*>(memchr(p, '{', end - p));
+    if (!q) {
+      outputString(StringPiece(p, end));
+      break;
+    }
+    outputString(StringPiece(p, q));
+    p = q + 1;
+
+    if (p == end) {
+      throw BadFormatArg("folly::format: '}' at end of format string");
+    }
+
+    // "{{" -> "{"
+    if (*p == '{') {
+      out(StringPiece(p, 1));
+      ++p;
+      continue;
+    }
+
+    // Format string
+    q = static_cast<const char*>(memchr(p, '}', end - p));
+    if (q == nullptr) {
+      throw BadFormatArg("folly::format: missing ending '}'");
+    }
+    FormatArg arg(StringPiece(p, q));
+    p = q + 1;
+
+    int argIndex = 0;
+    auto piece = arg.splitKey<true>();  // empty key component is okay
+    if (containerMode) {  // static
+      arg.enforce(arg.width != FormatArg::kDynamicWidth,
+                  "dynamic field width not supported in vformat()");
+      if (piece.empty()) {
+        arg.setNextIntKey(nextArg++);
+        hasDefaultArgIndex = true;
+      } else {
+        arg.setNextKey(piece);
+        hasExplicitArgIndex = true;
+      }
+    } else {
+      if (piece.empty()) {
+        if (arg.width == FormatArg::kDynamicWidth) {
+          arg.enforce(arg.widthIndex == FormatArg::kNoIndex,
+                      "cannot provide width arg index without value arg index");
+          int sizeArg = nextArg++;
+          arg.width = getSizeArg(sizeArg, arg);
+        }
+
+        argIndex = nextArg++;
+        hasDefaultArgIndex = true;
+      } else {
+        if (arg.width == FormatArg::kDynamicWidth) {
+          arg.enforce(arg.widthIndex != FormatArg::kNoIndex,
+                      "cannot provide value arg index without width arg index");
+          arg.width = getSizeArg(arg.widthIndex, arg);
+        }
+
+        try {
+          argIndex = to<int>(piece);
+        } catch (const std::out_of_range& e) {
+          arg.error("argument index must be integer");
+        }
+        arg.enforce(argIndex >= 0, "argument index must be non-negative");
+        hasExplicitArgIndex = true;
+      }
+    }
+
+    if (hasDefaultArgIndex && hasExplicitArgIndex) {
+      throw BadFormatArg(
+          "folly::format: may not have both default and explicit arg indexes");
+    }
+
+    doFormat(argIndex, arg, out);
+  }
+}
+
+template <class Derived, bool containerMode, class... Args>
+void writeTo(FILE* fp,
+             const BaseFormatter<Derived, containerMode, Args...>& formatter) {
+  auto writer = [fp] (StringPiece sp) {
+    size_t n = fwrite(sp.data(), 1, sp.size(), fp);
+    if (n < sp.size()) {
+      throwSystemError("Formatter writeTo", "fwrite failed");
+    }
+  };
+  formatter(writer);
+}
+
+namespace format_value {
+
+template <class FormatCallback>
+void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) {
+  if (arg.width != FormatArg::kDefaultWidth && arg.width < 0) {
+    throw BadFormatArg("folly::format: invalid width");
+  }
+  if (arg.precision != FormatArg::kDefaultPrecision && arg.precision < 0) {
+    throw BadFormatArg("folly::format: invalid precision");
+  }
+
+  // XXX: clang should be smart enough to not need the two static_cast<size_t>
+  // uses below given the above checks. If clang ever becomes that smart, we
+  // should remove the otherwise unnecessary warts.
+
+  if (arg.precision != FormatArg::kDefaultPrecision &&
+      val.size() > static_cast<size_t>(arg.precision)) {
+    val.reset(val.data(), arg.precision);
+  }
+
+  constexpr int padBufSize = 128;
+  char padBuf[padBufSize];
+
+  // Output padding, no more than padBufSize at once
+  auto pad = [&padBuf, &cb, padBufSize] (int chars) {
+    while (chars) {
+      int n = std::min(chars, padBufSize);
+      cb(StringPiece(padBuf, n));
+      chars -= n;
+    }
+  };
+
+  int padRemaining = 0;
+  if (arg.width != FormatArg::kDefaultWidth &&
+      val.size() < static_cast<size_t>(arg.width)) {
+    char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill;
+    int padChars = static_cast<int> (arg.width - val.size());
+    memset(padBuf, fill, std::min(padBufSize, padChars));
+
+    switch (arg.align) {
+    case FormatArg::Align::DEFAULT:
+    case FormatArg::Align::LEFT:
+      padRemaining = padChars;
+      break;
+    case FormatArg::Align::CENTER:
+      pad(padChars / 2);
+      padRemaining = padChars - padChars / 2;
+      break;
+    case FormatArg::Align::RIGHT:
+    case FormatArg::Align::PAD_AFTER_SIGN:
+      pad(padChars);
+      break;
+    default:
+      abort();
+      break;
+    }
+  }
+
+  cb(val);
+
+  if (padRemaining) {
+    pad(padRemaining);
+  }
+}
+
+template <class FormatCallback>
+void formatNumber(StringPiece val, int prefixLen, FormatArg& arg,
+                  FormatCallback& cb) {
+  // precision means something different for numbers
+  arg.precision = FormatArg::kDefaultPrecision;
+  if (arg.align == FormatArg::Align::DEFAULT) {
+    arg.align = FormatArg::Align::RIGHT;
+  } else if (prefixLen && arg.align == FormatArg::Align::PAD_AFTER_SIGN) {
+    // Split off the prefix, then do any padding if necessary
+    cb(val.subpiece(0, prefixLen));
+    val.advance(prefixLen);
+    arg.width = std::max(arg.width - prefixLen, 0);
+  }
+  format_value::formatString(val, arg, cb);
+}
+
+template <class FormatCallback,
+          class Derived,
+          bool containerMode,
+          class... Args>
+void formatFormatter(
+    const BaseFormatter<Derived, containerMode, Args...>& formatter,
+    FormatArg& arg,
+    FormatCallback& cb) {
+  if (arg.width == FormatArg::kDefaultWidth &&
+      arg.precision == FormatArg::kDefaultPrecision) {
+    // nothing to do
+    formatter(cb);
+  } else if (arg.align != FormatArg::Align::LEFT &&
+             arg.align != FormatArg::Align::DEFAULT) {
+    // We can only avoid creating a temporary string if we align left,
+    // as we'd need to know the size beforehand otherwise
+    format_value::formatString(formatter.fbstr(), arg, cb);
+  } else {
+    auto fn = [&arg, &cb] (StringPiece sp) mutable {
+      int sz = static_cast<int>(sp.size());
+      if (arg.precision != FormatArg::kDefaultPrecision) {
+        sz = std::min(arg.precision, sz);
+        sp.reset(sp.data(), sz);
+        arg.precision -= sz;
+      }
+      if (!sp.empty()) {
+        cb(sp);
+        if (arg.width != FormatArg::kDefaultWidth) {
+          arg.width = std::max(arg.width - sz, 0);
+        }
+      }
+    };
+    formatter(fn);
+    if (arg.width != FormatArg::kDefaultWidth && arg.width != 0) {
+      // Rely on formatString to do appropriate padding
+      format_value::formatString(StringPiece(), arg, cb);
+    }
+  }
+}
+
+}  // namespace format_value
+
+// Definitions for default FormatValue classes
+
+// Integral types (except bool)
+template <class T>
+class FormatValue<
+  T, typename std::enable_if<
+    std::is_integral<T>::value &&
+    !std::is_same<T, bool>::value>::type>
+  {
+ public:
+  explicit FormatValue(T val) : val_(val) { }
+
+  T getValue() const {
+    return val_;
+  }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    arg.validate(FormatArg::Type::INTEGER);
+    doFormat(arg, cb);
+  }
+
+  template <class FormatCallback>
+  void doFormat(FormatArg& arg, FormatCallback& cb) const {
+    char presentation = arg.presentation;
+    if (presentation == FormatArg::kDefaultPresentation) {
+      presentation = std::is_same<T, char>::value ? 'c' : 'd';
+    }
+
+    // Do all work as unsigned, we'll add the prefix ('0' or '0x' if necessary)
+    // and sign ourselves.
+    typedef typename std::make_unsigned<T>::type UT;
+    UT uval;
+    char sign;
+    if (std::is_signed<T>::value) {
+      if (folly::is_negative(val_)) {
+        uval = static_cast<UT>(-val_);
+        sign = '-';
+      } else {
+        uval = static_cast<UT>(val_);
+        switch (arg.sign) {
+        case FormatArg::Sign::PLUS_OR_MINUS:
+          sign = '+';
+          break;
+        case FormatArg::Sign::SPACE_OR_MINUS:
+          sign = ' ';
+          break;
+        default:
+          sign = '\0';
+          break;
+        }
+      }
+    } else {
+      uval = val_;
+      sign = '\0';
+
+      arg.enforce(arg.sign == FormatArg::Sign::DEFAULT,
+                  "sign specifications not allowed for unsigned values");
+    }
+
+    // max of:
+    // #x: 0x prefix + 16 bytes = 18 bytes
+    // #o: 0 prefix + 22 bytes = 23 bytes
+    // #b: 0b prefix + 64 bytes = 65 bytes
+    // ,d: 26 bytes (including thousands separators!)
+    // + nul terminator
+    // + 3 for sign and prefix shenanigans (see below)
+    constexpr size_t valBufSize = 69;
+    char valBuf[valBufSize];
+    char* valBufBegin = nullptr;
+    char* valBufEnd = nullptr;
+
+    int prefixLen = 0;
+    switch (presentation) {
+    case 'n': {
+      arg.enforce(!arg.basePrefix,
+                  "base prefix not allowed with '", presentation,
+                  "' specifier");
+
+      arg.enforce(!arg.thousandsSeparator,
+                  "cannot use ',' with the '", presentation,
+                  "' specifier");
+
+      valBufBegin = valBuf + 3;  // room for sign and base prefix
+#ifdef _MSC_VER
+      char valBuf2[valBufSize];
+      snprintf(valBuf2, valBufSize, "%ju", static_cast<uintmax_t>(uval));
+      int len = GetNumberFormat(
+        LOCALE_USER_DEFAULT,
+        0,
+        valBuf2,
+        nullptr,
+        valBufBegin,
+        (int)((valBuf + valBufSize) - valBufBegin)
+      );
+#else
+      int len = snprintf(valBufBegin, (valBuf + valBufSize) - valBufBegin,
+                         "%'ju", static_cast<uintmax_t>(uval));
+#endif
+      // valBufSize should always be big enough, so this should never
+      // happen.
+      assert(len < valBuf + valBufSize - valBufBegin);
+      valBufEnd = valBufBegin + len;
+      break;
+    }
+    case 'd':
+      arg.enforce(!arg.basePrefix,
+                  "base prefix not allowed with '", presentation,
+                  "' specifier");
+      valBufBegin = valBuf + 3;  // room for sign and base prefix
+
+      // Use uintToBuffer, faster than sprintf
+      valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin);
+      if (arg.thousandsSeparator) {
+        detail::insertThousandsGroupingUnsafe(valBufBegin, &valBufEnd);
+      }
+      break;
+    case 'c':
+      arg.enforce(!arg.basePrefix,
+                  "base prefix not allowed with '", presentation,
+                  "' specifier");
+      arg.enforce(!arg.thousandsSeparator,
+                  "thousands separator (',') not allowed with '",
+                  presentation, "' specifier");
+      valBufBegin = valBuf + 3;
+      *valBufBegin = static_cast<char>(uval);
+      valBufEnd = valBufBegin + 1;
+      break;
+    case 'o':
+    case 'O':
+      arg.enforce(!arg.thousandsSeparator,
+                  "thousands separator (',') not allowed with '",
+                  presentation, "' specifier");
+      valBufEnd = valBuf + valBufSize - 1;
+      valBufBegin = valBuf + detail::uintToOctal(valBuf, valBufSize - 1, uval);
+      if (arg.basePrefix) {
+        *--valBufBegin = '0';
+        prefixLen = 1;
+      }
+      break;
+    case 'x':
+      arg.enforce(!arg.thousandsSeparator,
+                  "thousands separator (',') not allowed with '",
+                  presentation, "' specifier");
+      valBufEnd = valBuf + valBufSize - 1;
+      valBufBegin = valBuf + detail::uintToHexLower(valBuf, valBufSize - 1,
+                                                    uval);
+      if (arg.basePrefix) {
+        *--valBufBegin = 'x';
+        *--valBufBegin = '0';
+        prefixLen = 2;
+      }
+      break;
+    case 'X':
+      arg.enforce(!arg.thousandsSeparator,
+                  "thousands separator (',') not allowed with '",
+                  presentation, "' specifier");
+      valBufEnd = valBuf + valBufSize - 1;
+      valBufBegin = valBuf + detail::uintToHexUpper(valBuf, valBufSize - 1,
+                                                    uval);
+      if (arg.basePrefix) {
+        *--valBufBegin = 'X';
+        *--valBufBegin = '0';
+        prefixLen = 2;
+      }
+      break;
+    case 'b':
+    case 'B':
+      arg.enforce(!arg.thousandsSeparator,
+                  "thousands separator (',') not allowed with '",
+                  presentation, "' specifier");
+      valBufEnd = valBuf + valBufSize - 1;
+      valBufBegin = valBuf + detail::uintToBinary(valBuf, valBufSize - 1,
+                                                  uval);
+      if (arg.basePrefix) {
+        *--valBufBegin = presentation;  // 0b or 0B
+        *--valBufBegin = '0';
+        prefixLen = 2;
+      }
+      break;
+    default:
+      arg.error("invalid specifier '", presentation, "'");
+    }
+
+    if (sign) {
+      *--valBufBegin = sign;
+      ++prefixLen;
+    }
+
+    format_value::formatNumber(StringPiece(valBufBegin, valBufEnd), prefixLen,
+                               arg, cb);
+  }
+
+ private:
+  T val_;
+};
+
+// Bool
+template <>
+class FormatValue<bool> {
+ public:
+  explicit FormatValue(bool val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    if (arg.presentation == FormatArg::kDefaultPresentation) {
+      arg.validate(FormatArg::Type::OTHER);
+      format_value::formatString(val_ ? "true" : "false", arg, cb);
+    } else {  // number
+      FormatValue<int>(val_).format(arg, cb);
+    }
+  }
+
+ private:
+  bool val_;
+};
+
+// double
+template <>
+class FormatValue<double> {
+ public:
+  explicit FormatValue(double val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    fbstring piece;
+    int prefixLen;
+    formatHelper(piece, prefixLen, arg);
+    format_value::formatNumber(piece, prefixLen, arg, cb);
+  }
+
+ private:
+  void formatHelper(fbstring& piece, int& prefixLen, FormatArg& arg) const;
+
+  double val_;
+};
+
+// float (defer to double)
+template <>
+class FormatValue<float> {
+ public:
+  explicit FormatValue(float val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    FormatValue<double>(val_).format(arg, cb);
+  }
+
+ private:
+  float val_;
+};
+
+// Sring-y types (implicitly convertible to StringPiece, except char*)
+template <class T>
+class FormatValue<
+  T, typename std::enable_if<
+      (!std::is_pointer<T>::value ||
+       !std::is_same<char, typename std::decay<
+          typename std::remove_pointer<T>::type>::type>::value) &&
+      std::is_convertible<T, StringPiece>::value>::type>
+  {
+ public:
+  explicit FormatValue(StringPiece val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    if (arg.keyEmpty()) {
+      arg.validate(FormatArg::Type::OTHER);
+      arg.enforce(arg.presentation == FormatArg::kDefaultPresentation ||
+                  arg.presentation == 's',
+                  "invalid specifier '", arg.presentation, "'");
+      format_value::formatString(val_, arg, cb);
+    } else {
+      FormatValue<char>(val_.at(arg.splitIntKey())).format(arg, cb);
+    }
+  }
+
+ private:
+  StringPiece val_;
+};
+
+// Null
+template <>
+class FormatValue<std::nullptr_t> {
+ public:
+  explicit FormatValue(std::nullptr_t) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    arg.validate(FormatArg::Type::OTHER);
+    arg.enforce(arg.presentation == FormatArg::kDefaultPresentation,
+                "invalid specifier '", arg.presentation, "'");
+    format_value::formatString("(null)", arg, cb);
+  }
+};
+
+// Partial specialization of FormatValue for char*
+template <class T>
+class FormatValue<
+  T*,
+  typename std::enable_if<
+      std::is_same<char, typename std::decay<T>::type>::value>::type>
+  {
+ public:
+  explicit FormatValue(T* val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    if (arg.keyEmpty()) {
+      if (!val_) {
+        FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
+      } else {
+        FormatValue<StringPiece>(val_).format(arg, cb);
+      }
+    } else {
+      FormatValue<typename std::decay<T>::type>(
+          val_[arg.splitIntKey()]).format(arg, cb);
+    }
+  }
+
+ private:
+  T* val_;
+};
+
+// Partial specialization of FormatValue for void*
+template <class T>
+class FormatValue<
+  T*,
+  typename std::enable_if<
+      std::is_same<void, typename std::decay<T>::type>::value>::type>
+  {
+ public:
+  explicit FormatValue(T* val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    if (!val_) {
+      FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
+    } else {
+      // Print as a pointer, in hex.
+      arg.validate(FormatArg::Type::OTHER);
+      arg.enforce(arg.presentation == FormatArg::kDefaultPresentation,
+                  "invalid specifier '", arg.presentation, "'");
+      arg.basePrefix = true;
+      arg.presentation = 'x';
+      if (arg.align == FormatArg::Align::DEFAULT) {
+        arg.align = FormatArg::Align::LEFT;
+      }
+      FormatValue<uintptr_t>(
+          reinterpret_cast<uintptr_t>(val_)).doFormat(arg, cb);
+    }
+  }
+
+ private:
+  T* val_;
+};
+
+template <class T, class = void>
+class TryFormatValue {
+ public:
+  template <class FormatCallback>
+  static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) {
+    arg.error("No formatter available for this type");
+  }
+};
+
+template <class T>
+class TryFormatValue<
+  T,
+  typename std::enable_if<
+      0 < sizeof(FormatValue<typename std::decay<T>::type>)>::type>
+  {
+ public:
+  template <class FormatCallback>
+  static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) {
+    FormatValue<typename std::decay<T>::type>(value).format(arg, cb);
+  }
+};
+
+// Partial specialization of FormatValue for other pointers
+template <class T>
+class FormatValue<
+  T*,
+  typename std::enable_if<
+      !std::is_same<char, typename std::decay<T>::type>::value &&
+      !std::is_same<void, typename std::decay<T>::type>::value>::type>
+  {
+ public:
+  explicit FormatValue(T* val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    if (arg.keyEmpty()) {
+      FormatValue<void*>((void*)val_).format(arg, cb);
+    } else {
+      TryFormatValue<T>::formatOrFail(val_[arg.splitIntKey()], arg, cb);
+    }
+  }
+ private:
+  T* val_;
+};
+
+namespace detail {
+
+// std::array
+template <class T, size_t N>
+struct IndexableTraits<std::array<T, N>>
+  : public IndexableTraitsSeq<std::array<T, N>> {
+};
+
+// std::vector
+template <class T, class A>
+struct IndexableTraits<std::vector<T, A>>
+  : public IndexableTraitsSeq<std::vector<T, A>> {
+};
+
+// std::deque
+template <class T, class A>
+struct IndexableTraits<std::deque<T, A>>
+  : public IndexableTraitsSeq<std::deque<T, A>> {
+};
+
+// std::map with integral keys
+template <class K, class T, class C, class A>
+struct IndexableTraits<
+  std::map<K, T, C, A>,
+  typename std::enable_if<std::is_integral<K>::value>::type>
+  : public IndexableTraitsAssoc<std::map<K, T, C, A>> {
+};
+
+// std::unordered_map with integral keys
+template <class K, class T, class H, class E, class A>
+struct IndexableTraits<
+  std::unordered_map<K, T, H, E, A>,
+  typename std::enable_if<std::is_integral<K>::value>::type>
+  : public IndexableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {
+};
+
+}  // namespace detail
+
+// Partial specialization of FormatValue for integer-indexable containers
+template <class T>
+class FormatValue<
+  T,
+  typename detail::IndexableTraits<T>::enabled> {
+ public:
+  explicit FormatValue(const T& val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    FormatValue<typename std::decay<
+      typename detail::IndexableTraits<T>::value_type>::type>(
+        detail::IndexableTraits<T>::at(
+            val_, arg.splitIntKey())).format(arg, cb);
+  }
+
+ private:
+  const T& val_;
+};
+
+template <class Container, class Value>
+class FormatValue<
+  detail::DefaultValueWrapper<Container, Value>,
+  typename detail::IndexableTraits<Container>::enabled> {
+ public:
+  explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)
+    : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    FormatValue<typename std::decay<
+      typename detail::IndexableTraits<Container>::value_type>::type>(
+          detail::IndexableTraits<Container>::at(
+              val_.container,
+              arg.splitIntKey(),
+              val_.defaultValue)).format(arg, cb);
+  }
+
+ private:
+  const detail::DefaultValueWrapper<Container, Value>& val_;
+};
+
+namespace detail {
+
+// Define enabled, key_type, convert from StringPiece to the key types
+// that we support
+template <class T> struct KeyFromStringPiece;
+
+// std::string
+template <>
+struct KeyFromStringPiece<std::string> : public FormatTraitsBase {
+  typedef std::string key_type;
+  static std::string convert(StringPiece s) {
+    return s.toString();
+  }
+  typedef void enabled;
+};
+
+// fbstring
+template <>
+struct KeyFromStringPiece<fbstring> : public FormatTraitsBase {
+  typedef fbstring key_type;
+  static fbstring convert(StringPiece s) {
+    return s.toFbstring();
+  }
+};
+
+// StringPiece
+template <>
+struct KeyFromStringPiece<StringPiece> : public FormatTraitsBase {
+  typedef StringPiece key_type;
+  static StringPiece convert(StringPiece s) {
+    return s;
+  }
+};
+
+// Base class for associative types keyed by strings
+template <class T> struct KeyableTraitsAssoc : public FormatTraitsBase {
+  typedef typename T::key_type key_type;
+  typedef typename T::value_type::second_type value_type;
+  static const value_type& at(const T& map, StringPiece key) {
+    return map.at(KeyFromStringPiece<key_type>::convert(key));
+  }
+  static const value_type& at(const T& map, StringPiece key,
+                              const value_type& dflt) {
+    auto pos = map.find(KeyFromStringPiece<key_type>::convert(key));
+    return pos != map.end() ? pos->second : dflt;
+  }
+};
+
+// Define enabled, key_type, value_type, at() for supported string-keyed
+// types
+template <class T, class Enabled=void> struct KeyableTraits;
+
+// std::map with string key
+template <class K, class T, class C, class A>
+struct KeyableTraits<
+  std::map<K, T, C, A>,
+  typename KeyFromStringPiece<K>::enabled>
+  : public KeyableTraitsAssoc<std::map<K, T, C, A>> {
+};
+
+// std::unordered_map with string key
+template <class K, class T, class H, class E, class A>
+struct KeyableTraits<
+  std::unordered_map<K, T, H, E, A>,
+  typename KeyFromStringPiece<K>::enabled>
+  : public KeyableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {
+};
+
+}  // namespace detail
+
+// Partial specialization of FormatValue for string-keyed containers
+template <class T>
+class FormatValue<
+  T,
+  typename detail::KeyableTraits<T>::enabled> {
+ public:
+  explicit FormatValue(const T& val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    FormatValue<typename std::decay<
+      typename detail::KeyableTraits<T>::value_type>::type>(
+        detail::KeyableTraits<T>::at(
+            val_, arg.splitKey())).format(arg, cb);
+  }
+
+ private:
+  const T& val_;
+};
+
+template <class Container, class Value>
+class FormatValue<
+  detail::DefaultValueWrapper<Container, Value>,
+  typename detail::KeyableTraits<Container>::enabled> {
+ public:
+  explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)
+    : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    FormatValue<typename std::decay<
+      typename detail::KeyableTraits<Container>::value_type>::type>(
+          detail::KeyableTraits<Container>::at(
+              val_.container,
+              arg.splitKey(),
+              val_.defaultValue)).format(arg, cb);
+  }
+
+ private:
+  const detail::DefaultValueWrapper<Container, Value>& val_;
+};
+
+// Partial specialization of FormatValue for pairs
+template <class A, class B>
+class FormatValue<std::pair<A, B>> {
+ public:
+  explicit FormatValue(const std::pair<A, B>& val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    int key = arg.splitIntKey();
+    switch (key) {
+    case 0:
+      FormatValue<typename std::decay<A>::type>(val_.first).format(arg, cb);
+      break;
+    case 1:
+      FormatValue<typename std::decay<B>::type>(val_.second).format(arg, cb);
+      break;
+    default:
+      arg.error("invalid index for pair");
+    }
+  }
+
+ private:
+  const std::pair<A, B>& val_;
+};
+
+// Partial specialization of FormatValue for tuples
+template <class... Args>
+class FormatValue<std::tuple<Args...>> {
+  typedef std::tuple<Args...> Tuple;
+ public:
+  explicit FormatValue(const Tuple& val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    int key = arg.splitIntKey();
+    arg.enforce(key >= 0, "tuple index must be non-negative");
+    doFormat(key, arg, cb);
+  }
+
+ private:
+  static constexpr size_t valueCount = std::tuple_size<Tuple>::value;
+
+  template <size_t K, class Callback>
+  typename std::enable_if<K == valueCount>::type
+  doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
+    arg.enforce("tuple index out of range, max=", i);
+  }
+
+  template <size_t K, class Callback>
+  typename std::enable_if<(K < valueCount)>::type
+  doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
+    if (i == K) {
+      FormatValue<typename std::decay<
+        typename std::tuple_element<K, Tuple>::type>::type>(
+          std::get<K>(val_)).format(arg, cb);
+    } else {
+      doFormatFrom<K+1>(i, arg, cb);
+    }
+  }
+
+  template <class Callback>
+  void doFormat(size_t i, FormatArg& arg, Callback& cb) const {
+    return doFormatFrom<0>(i, arg, cb);
+  }
+
+  const Tuple& val_;
+};
+
+// Partial specialization of FormatValue for nested Formatters
+template <bool containerMode, class... Args,
+          template <bool, class...> class F>
+class FormatValue<F<containerMode, Args...>,
+                  typename std::enable_if<detail::IsFormatter<
+                      F<containerMode, Args...>>::value>::type> {
+  typedef typename F<containerMode, Args...>::BaseType FormatterValue;
+
+ public:
+  explicit FormatValue(const FormatterValue& f) : f_(f) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    format_value::formatFormatter(f_, arg, cb);
+  }
+ private:
+  const FormatterValue& f_;
+};
+
+/**
+ * Formatter objects can be appended to strings, and therefore they're
+ * compatible with folly::toAppend and folly::to.
+ */
+template <class Tgt, class Derived, bool containerMode, class... Args>
+typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
+    const BaseFormatter<Derived, containerMode, Args...>& value, Tgt* result) {
+  value.appendTo(*result);
+}
+
+}  // namespace folly
+
+#pragma GCC diagnostic pop
diff --git a/faux-folly/folly/Format.cpp b/faux-folly/folly/Format.cpp
new file mode 100644
index 0000000..da8f69c
--- /dev/null
+++ b/faux-folly/folly/Format.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2015 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/Format.h>
+
+#include <double-conversion/double-conversion.h>
+
+namespace folly {
+namespace detail {
+
+extern const FormatArg::Align formatAlignTable[];
+extern const FormatArg::Sign formatSignTable[];
+
+template <typename T>
+inline constexpr
+T bespoke_max(const T a, const T b)
+{
+    return (a > b) ? a : b;
+}
+
+template <typename T>
+inline constexpr
+T bespoke_max(const T a, const T b, const T c)
+{
+    return bespoke_max(bespoke_max(a, b), c);
+}
+
+}  // namespace detail
+
+using namespace folly::detail;
+
+void FormatValue<double>::formatHelper(
+    fbstring& piece, int& prefixLen, FormatArg& arg) const {
+  using ::double_conversion::DoubleToStringConverter;
+  using ::double_conversion::StringBuilder;
+
+  arg.validate(FormatArg::Type::FLOAT);
+
+  if (arg.presentation == FormatArg::kDefaultPresentation) {
+    arg.presentation = 'g';
+  }
+
+  const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf";
+  const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan";
+  char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';
+
+  if (arg.precision == FormatArg::kDefaultPrecision) {
+    arg.precision = 6;
+  }
+
+  // 2+: for null terminator and optional sign shenanigans.
+  constexpr unsigned buflen = 2 + bespoke_max(
+          (2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +
+           DoubleToStringConverter::kMaxFixedDigitsAfterPoint),
+          (8 + DoubleToStringConverter::kMaxExponentialDigits),
+          (7 + DoubleToStringConverter::kMaxPrecisionDigits));
+  char buf[buflen];
+  StringBuilder builder(buf + 1, static_cast<int> (sizeof(buf) - 1));
+
+  char plusSign;
+  switch (arg.sign) {
+  case FormatArg::Sign::PLUS_OR_MINUS:
+    plusSign = '+';
+    break;
+  case FormatArg::Sign::SPACE_OR_MINUS:
+    plusSign = ' ';
+    break;
+  default:
+    plusSign = '\0';
+    break;
+  };
+
+  auto flags =
+      DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
+      (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
+                       : 0);
+
+  double val = val_;
+  switch (arg.presentation) {
+  case '%':
+    val *= 100;
+  case 'f':
+  case 'F':
+    {
+      if (arg.precision >
+          DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
+        arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
+      }
+      DoubleToStringConverter conv(flags,
+                                   infinitySymbol,
+                                   nanSymbol,
+                                   exponentSymbol,
+                                   -4,
+                                   arg.precision,
+                                   0,
+                                   0);
+      arg.enforce(conv.ToFixed(val, arg.precision, &builder),
+                  "fixed double conversion failed");
+    }
+    break;
+  case 'e':
+  case 'E':
+    {
+      if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {
+        arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
+      }
+
+      DoubleToStringConverter conv(flags,
+                                   infinitySymbol,
+                                   nanSymbol,
+                                   exponentSymbol,
+                                   -4,
+                                   arg.precision,
+                                   0,
+                                   0);
+      arg.enforce(conv.ToExponential(val, arg.precision, &builder));
+    }
+    break;
+  case 'n':  // should be locale-aware, but isn't
+  case 'g':
+  case 'G':
+    {
+      if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {
+        arg.precision = DoubleToStringConverter::kMinPrecisionDigits;
+      } else if (arg.precision >
+                 DoubleToStringConverter::kMaxPrecisionDigits) {
+        arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
+      }
+      DoubleToStringConverter conv(flags,
+                                   infinitySymbol,
+                                   nanSymbol,
+                                   exponentSymbol,
+                                   -4,
+                                   arg.precision,
+                                   0,
+                                   0);
+      arg.enforce(conv.ToShortest(val, &builder));
+    }
+    break;
+  default:
+    arg.error("invalid specifier '", arg.presentation, "'");
+  }
+
+  int len = builder.position();
+  builder.Finalize();
+  DCHECK_GT(len, 0);
+
+  // Add '+' or ' ' sign if needed
+  char* p = buf + 1;
+  // anything that's neither negative nor nan
+  prefixLen = 0;
+  if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {
+    *--p = plusSign;
+    ++len;
+    prefixLen = 1;
+  } else if (*p == '-') {
+    prefixLen = 1;
+  }
+
+  piece = fbstring(p, len);
+}
+
+
+void FormatArg::initSlow() {
+  auto b = fullArgString.begin();
+  auto end = fullArgString.end();
+
+  // Parse key
+  auto p = static_cast<const char*>(memchr(b, ':', end - b));
+  if (!p) {
+    key_ = StringPiece(b, end);
+    return;
+  }
+  key_ = StringPiece(b, p);
+
+  if (*p == ':') {
+    // parse format spec
+    if (++p == end) return;
+
+    // fill/align, or just align
+    Align a;
+    if (p + 1 != end &&
+        (a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=
+        Align::INVALID) {
+      fill = *p;
+      align = a;
+      p += 2;
+      if (p == end) return;
+    } else if ((a = formatAlignTable[static_cast<unsigned char>(*p)]) !=
+               Align::INVALID) {
+      align = a;
+      if (++p == end) return;
+    }
+
+    Sign s;
+    unsigned char uSign = static_cast<unsigned char>(*p);
+    if ((s = formatSignTable[uSign]) != Sign::INVALID) {
+      sign = s;
+      if (++p == end) return;
+    }
+
+    if (*p == '#') {
+      basePrefix = true;
+      if (++p == end) return;
+    }
+
+    if (*p == '0') {
+      enforce(align == Align::DEFAULT, "alignment specified twice");
+      fill = '0';
+      align = Align::PAD_AFTER_SIGN;
+      if (++p == end) return;
+    }
+
+    auto readInt = [&] {
+      auto const b = p;
+      do {
+        ++p;
+      } while (p != end && *p >= '0' && *p <= '9');
+      return to<int>(StringPiece(b, p));
+    };
+
+    if (*p == '*') {
+      width = kDynamicWidth;
+      ++p;
+
+      if (p == end) return;
+
+      if (*p >= '0' && *p <= '9') widthIndex = readInt();
+
+      if (p == end) return;
+    } else if (*p >= '0' && *p <= '9') {
+      width = readInt();
+
+      if (p == end) return;
+    }
+
+    if (*p == ',') {
+      thousandsSeparator = true;
+      if (++p == end) return;
+    }
+
+    if (*p == '.') {
+      auto b = ++p;
+      while (p != end && *p >= '0' && *p <= '9') {
+        ++p;
+      }
+      if (p != b) {
+        precision = to<int>(StringPiece(b, p));
+        if (p != end && *p == '.') {
+          trailingDot = true;
+          ++p;
+        }
+      } else {
+        trailingDot = true;
+      }
+
+      if (p == end) return;
+    }
+
+    presentation = *p;
+    if (++p == end) return;
+  }
+
+  error("extra characters in format string");
+}
+
+void FormatArg::validate(Type type) const {
+  enforce(keyEmpty(), "index not allowed");
+  switch (type) {
+  case Type::INTEGER:
+    enforce(precision == kDefaultPrecision,
+            "precision not allowed on integers");
+    break;
+  case Type::FLOAT:
+    enforce(!basePrefix,
+            "base prefix ('#') specifier only allowed on integers");
+    enforce(!thousandsSeparator,
+            "thousands separator (',') only allowed on integers");
+    break;
+  case Type::OTHER:
+    enforce(align != Align::PAD_AFTER_SIGN,
+            "'='alignment only allowed on numbers");
+    enforce(sign == Sign::DEFAULT,
+            "sign specifier only allowed on numbers");
+    enforce(!basePrefix,
+            "base prefix ('#') specifier only allowed on integers");
+    enforce(!thousandsSeparator,
+            "thousands separator (',') only allowed on integers");
+    break;
+  }
+}
+
+namespace detail {
+void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {
+  uint32_t remaining_digits = *end_buffer - start_buffer;
+  uint32_t separator_size = (remaining_digits - 1) / 3;
+  uint32_t result_size = remaining_digits + separator_size;
+  *end_buffer = *end_buffer + separator_size;
+
+  // get the end of the new string with the separators
+  uint32_t buffer_write_index = result_size - 1;
+  uint32_t buffer_read_index = remaining_digits - 1;
+  start_buffer[buffer_write_index + 1] = 0;
+
+  bool done = false;
+  uint32_t next_group_size = 3;
+
+  while (!done) {
+    uint32_t current_group_size = std::max<uint32_t>(1,
+      std::min<uint32_t>(remaining_digits, next_group_size));
+
+    // write out the current group's digits to the buffer index
+    for (uint32_t i = 0; i < current_group_size; i++) {
+      start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];
+    }
+
+    // if not finished, write the separator before the next group
+    if (buffer_write_index < buffer_write_index + 1) {
+      start_buffer[buffer_write_index--] = ',';
+    } else {
+      done = true;
+    }
+
+    remaining_digits -= current_group_size;
+  }
+}
+} // detail
+
+}  // namespace folly
diff --git a/faux-folly/folly/Format.h b/faux-folly/folly/Format.h
new file mode 100644
index 0000000..e3134a3
--- /dev/null
+++ b/faux-folly/folly/Format.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_FORMAT_H_
+#define FOLLY_FORMAT_H_
+
+#include <cstdio>
+#include <tuple>
+#include <type_traits>
+
+#include <folly/Conv.h>
+#include <folly/Range.h>
+#include <folly/Traits.h>
+#include <folly/String.h>
+#include <folly/FormatArg.h>
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
+namespace folly {
+
+// forward declarations
+template <bool containerMode, class... Args> class Formatter;
+template <class... Args>
+Formatter<false, Args...> format(StringPiece fmt, Args&&... args);
+template <class C>
+Formatter<true, C> vformat(StringPiece fmt, C&& container);
+template <class T, class Enable=void> class FormatValue;
+
+// meta-attribute to identify formatters in this sea of template weirdness
+namespace detail {
+class FormatterTag {};
+};
+
+/**
+ * Formatter class.
+ *
+ * Note that this class is tricky, as it keeps *references* to its arguments
+ * (and doesn't copy the passed-in format string).  Thankfully, you can't use
+ * this directly, you have to use format(...) below.
+ */
+
+/* BaseFormatter class. Currently, the only behavior that can be
+ * overridden is the actual formatting of positional parameters in
+ * `doFormatArg`. The Formatter class provides the default implementation.
+ */
+template <class Derived, bool containerMode, class... Args>
+class BaseFormatter {
+ public:
+  /**
+   * Append to output.  out(StringPiece sp) may be called (more than once)
+   */
+  template <class Output>
+  void operator()(Output& out) const;
+
+  /**
+   * Append to a string.
+   */
+  template <class Str>
+  typename std::enable_if<IsSomeString<Str>::value>::type
+  appendTo(Str& str) const {
+    auto appender = [&str] (StringPiece s) { str.append(s.data(), s.size()); };
+    (*this)(appender);
+  }
+
+  /**
+   * Conversion to string
+   */
+  std::string str() const {
+    std::string s;
+    appendTo(s);
+    return s;
+  }
+
+  /**
+   * Conversion to fbstring
+   */
+  fbstring fbstr() const {
+    fbstring s;
+    appendTo(s);
+    return s;
+  }
+
+  /**
+   * metadata to identify generated children of BaseFormatter
+   */
+  typedef detail::FormatterTag IsFormatter;
+  typedef BaseFormatter BaseType;
+
+ private:
+  typedef std::tuple<FormatValue<
+      typename std::decay<Args>::type>...> ValueTuple;
+  static constexpr size_t valueCount = std::tuple_size<ValueTuple>::value;
+
+  template <size_t K, class Callback>
+  typename std::enable_if<K == valueCount>::type
+  doFormatFrom(size_t i, FormatArg& arg, Callback& /*cb*/) const {
+    arg.error("argument index out of range, max=", i);
+  }
+
+  template <size_t K, class Callback>
+  typename std::enable_if<(K < valueCount)>::type
+  doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
+    if (i == K) {
+      static_cast<const Derived*>(this)->template doFormatArg<K>(arg, cb);
+    } else {
+      doFormatFrom<K+1>(i, arg, cb);
+    }
+  }
+
+  template <class Callback>
+  void doFormat(size_t i, FormatArg& arg, Callback& cb) const {
+    return doFormatFrom<0>(i, arg, cb);
+  }
+
+  template <size_t K>
+  typename std::enable_if<K == valueCount, int>::type
+  getSizeArgFrom(size_t i, const FormatArg& arg) const {
+    arg.error("argument index out of range, max=", i);
+  }
+
+  template <class T>
+  typename std::enable_if<std::is_integral<T>::value &&
+                          !std::is_same<T, bool>::value, int>::type
+  getValue(const FormatValue<T>& format, const FormatArg&) const {
+    return static_cast<int>(format.getValue());
+  }
+
+  template <class T>
+  typename std::enable_if<!std::is_integral<T>::value ||
+                          std::is_same<T, bool>::value, int>::type
+  getValue(const FormatValue<T>&, const FormatArg& arg) const {
+    arg.error("dynamic field width argument must be integral");
+  }
+
+  template <size_t K>
+  typename std::enable_if<K < valueCount, int>::type
+  getSizeArgFrom(size_t i, const FormatArg& arg) const {
+    if (i == K) {
+      return getValue(std::get<K>(values_), arg);
+    }
+    return getSizeArgFrom<K+1>(i, arg);
+  }
+
+  int getSizeArg(size_t i, const FormatArg& arg) const {
+    return getSizeArgFrom<0>(i, arg);
+  }
+
+  StringPiece str_;
+
+ protected:
+  explicit BaseFormatter(StringPiece str, Args&&... args);
+
+  // Not copyable
+  BaseFormatter(const BaseFormatter&) = delete;
+  BaseFormatter& operator=(const BaseFormatter&) = delete;
+
+  // Movable, but the move constructor and assignment operator are private,
+  // for the exclusive use of format() (below).  This way, you can't create
+  // a Formatter object, but can handle references to it (for streaming,
+  // conversion to string, etc) -- which is good, as Formatter objects are
+  // dangerous (they hold references, possibly to temporaries)
+  BaseFormatter(BaseFormatter&&) = default;
+  BaseFormatter& operator=(BaseFormatter&&) = default;
+
+  ValueTuple values_;
+};
+
+template <bool containerMode, class... Args>
+class Formatter : public BaseFormatter<Formatter<containerMode, Args...>,
+                                       containerMode,
+                                       Args...> {
+ private:
+  explicit Formatter(StringPiece& str, Args&&... args)
+      : BaseFormatter<Formatter<containerMode, Args...>,
+                      containerMode,
+                      Args...>(str, std::forward<Args>(args)...) {}
+
+  template <size_t K, class Callback>
+  void doFormatArg(FormatArg& arg, Callback& cb) const {
+    std::get<K>(this->values_).format(arg, cb);
+  }
+
+  friend class BaseFormatter<Formatter<containerMode, Args...>,
+                             containerMode,
+                             Args...>;
+
+  template <class... A>
+  friend Formatter<false, A...> format(StringPiece fmt, A&&... arg);
+  template <class C>
+  friend Formatter<true, C> vformat(StringPiece fmt, C&& container);
+};
+
+/**
+ * Formatter objects can be written to streams.
+ */
+template<bool containerMode, class... Args>
+std::ostream& operator<<(std::ostream& out,
+                         const Formatter<containerMode, Args...>& formatter) {
+  auto writer = [&out] (StringPiece sp) { out.write(sp.data(), sp.size()); };
+  formatter(writer);
+  return out;
+}
+
+/**
+ * Formatter objects can be written to stdio FILEs.
+ */
+template <class Derived, bool containerMode, class... Args>
+void writeTo(FILE* fp,
+             const BaseFormatter<Derived, containerMode, Args...>& formatter);
+
+/**
+ * Create a formatter object.
+ *
+ * std::string formatted = format("{} {}", 23, 42).str();
+ * LOG(INFO) << format("{} {}", 23, 42);
+ * writeTo(stdout, format("{} {}", 23, 42));
+ */
+template <class... Args>
+Formatter<false, Args...> format(StringPiece fmt, Args&&... args) {
+  return Formatter<false, Args...>(
+      fmt, std::forward<Args>(args)...);
+}
+
+/**
+ * Like format(), but immediately returns the formatted string instead of an
+ * intermediate format object.
+ */
+template <class... Args>
+inline std::string sformat(StringPiece fmt, Args&&... args) {
+  return format(fmt, std::forward<Args>(args)...).str();
+}
+
+/**
+ * Create a formatter object that takes one argument (of container type)
+ * and uses that container to get argument values from.
+ *
+ * std::map<string, string> map { {"hello", "world"}, {"answer", "42"} };
+ *
+ * The following are equivalent:
+ * format("{0[hello]} {0[answer]}", map);
+ *
+ * vformat("{hello} {answer}", map);
+ *
+ * but the latter is cleaner.
+ */
+template <class Container>
+Formatter<true, Container> vformat(StringPiece fmt, Container&& container) {
+  return Formatter<true, Container>(
+      fmt, std::forward<Container>(container));
+}
+
+/**
+ * Like vformat(), but immediately returns the formatted string instead of an
+ * intermediate format object.
+ */
+template <class Container>
+inline std::string svformat(StringPiece fmt, Container&& container) {
+  return vformat(fmt, std::forward<Container>(container)).str();
+}
+
+/**
+ * Wrap a sequence or associative container so that out-of-range lookups
+ * return a default value rather than throwing an exception.
+ *
+ * Usage:
+ * format("[no_such_key"], defaulted(map, 42))  -> 42
+ */
+namespace detail {
+template <class Container, class Value> struct DefaultValueWrapper {
+  DefaultValueWrapper(const Container& container, const Value& defaultValue)
+    : container(container),
+      defaultValue(defaultValue) {
+  }
+
+  const Container& container;
+  const Value& defaultValue;
+};
+}  // namespace
+
+template <class Container, class Value>
+detail::DefaultValueWrapper<Container, Value>
+defaulted(const Container& c, const Value& v) {
+  return detail::DefaultValueWrapper<Container, Value>(c, v);
+}
+
+/**
+ * Append formatted output to a string.
+ *
+ * std::string foo;
+ * format(&foo, "{} {}", 42, 23);
+ *
+ * Shortcut for toAppend(format(...), &foo);
+ */
+template <class Str, class... Args>
+typename std::enable_if<IsSomeString<Str>::value>::type
+format(Str* out, StringPiece fmt, Args&&... args) {
+  format(fmt, std::forward<Args>(args)...).appendTo(*out);
+}
+
+/**
+ * Append vformatted output to a string.
+ */
+template <class Str, class Container>
+typename std::enable_if<IsSomeString<Str>::value>::type
+vformat(Str* out, StringPiece fmt, Container&& container) {
+  vformat(fmt, std::forward<Container>(container)).appendTo(*out);
+}
+
+/**
+ * Utilities for all format value specializations.
+ */
+namespace format_value {
+
+/**
+ * Format a string in "val", obeying appropriate alignment, padding, width,
+ * and precision.  Treats Align::DEFAULT as Align::LEFT, and
+ * Align::PAD_AFTER_SIGN as Align::RIGHT; use formatNumber for
+ * number-specific formatting.
+ */
+template <class FormatCallback>
+void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb);
+
+/**
+ * Format a number in "val"; the first prefixLen characters form the prefix
+ * (sign, "0x" base prefix, etc) which must be left-aligned if the alignment
+ * is Align::PAD_AFTER_SIGN.  Treats Align::DEFAULT as Align::LEFT.  Ignores
+ * arg.precision, as that has a different meaning for numbers (not "maximum
+ * field width")
+ */
+template <class FormatCallback>
+void formatNumber(StringPiece val, int prefixLen, FormatArg& arg,
+                  FormatCallback& cb);
+
+
+/**
+ * Format a Formatter object recursively.  Behaves just like
+ * formatString(fmt.str(), arg, cb); but avoids creating a temporary
+ * string if possible.
+ */
+template <class FormatCallback,
+          class Derived,
+          bool containerMode,
+          class... Args>
+void formatFormatter(
+    const BaseFormatter<Derived, containerMode, Args...>& formatter,
+    FormatArg& arg,
+    FormatCallback& cb);
+
+}  // namespace format_value
+
+/*
+ * Specialize folly::FormatValue for your type.
+ *
+ * FormatValue<T> is constructed with a (reference-collapsed) T&&, which is
+ * guaranteed to stay alive until the FormatValue object is destroyed, so you
+ * may keep a reference (or pointer) to it instead of making a copy.
+ *
+ * You must define
+ *   template <class Callback>
+ *   void format(FormatArg& arg, Callback& cb) const;
+ * with the following semantics: format the value using the given argument.
+ *
+ * arg is given by non-const reference for convenience -- it won't be reused,
+ * so feel free to modify it in place if necessary.  (For example, wrap an
+ * existing conversion but change the default, or remove the "key" when
+ * extracting an element from a container)
+ *
+ * Call the callback to append data to the output.  You may call the callback
+ * as many times as you'd like (or not at all, if you want to output an
+ * empty string)
+ */
+
+namespace detail {
+
+template <class T, class Enable = void>
+struct IsFormatter : public std::false_type {};
+
+template <class T>
+struct IsFormatter<
+    T,
+    typename std::enable_if<
+        std::is_same<typename T::IsFormatter, detail::FormatterTag>::value>::
+        type> : public std::true_type {};
+} // folly::detail
+
+// Deprecated API. formatChecked() et. al. now behave identically to their
+// non-Checked counterparts.
+template <class... Args>
+Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) {
+  return format(fmt, std::forward<Args>(args)...);
+}
+template <class... Args>
+inline std::string sformatChecked(StringPiece fmt, Args&&... args) {
+  return formatChecked(fmt, std::forward<Args>(args)...).str();
+}
+template <class Container>
+Formatter<true, Container> vformatChecked(StringPiece fmt,
+                                          Container&& container) {
+  return vformat(fmt, std::forward<Container>(container));
+}
+template <class Container>
+inline std::string svformatChecked(StringPiece fmt, Container&& container) {
+  return vformatChecked(fmt, std::forward<Container>(container)).str();
+}
+template <class Str, class... Args>
+typename std::enable_if<IsSomeString<Str>::value>::type
+formatChecked(Str* out, StringPiece fmt, Args&&... args) {
+  formatChecked(fmt, std::forward<Args>(args)...).appendTo(*out);
+}
+template <class Str, class Container>
+typename std::enable_if<IsSomeString<Str>::value>::type
+vformatChecked(Str* out, StringPiece fmt, Container&& container) {
+  vformatChecked(fmt, std::forward<Container>(container)).appendTo(*out);
+}
+
+}  // namespace folly
+
+#include <folly/Format-inl.h>
+
+#pragma GCC diagnostic pop
+
+#endif /* FOLLY_FORMAT_H_ */
diff --git a/faux-folly/folly/FormatArg.h b/faux-folly/folly/FormatArg.h
new file mode 100644
index 0000000..c4d9473
--- /dev/null
+++ b/faux-folly/folly/FormatArg.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_FORMATARG_H_
+#define FOLLY_FORMATARG_H_
+
+#include <stdexcept>
+#include <folly/Conv.h>
+#include <folly/Likely.h>
+#include <folly/Portability.h>
+#include <folly/Range.h>
+
+namespace folly {
+
+class BadFormatArg : public std::invalid_argument {
+ public:
+  explicit BadFormatArg(const std::string& msg)
+    : std::invalid_argument(msg) {}
+};
+
+/**
+ * Parsed format argument.
+ */
+struct FormatArg {
+  /**
+   * Parse a format argument from a string.  Keeps a reference to the
+   * passed-in string -- does not copy the given characters.
+   */
+  explicit FormatArg(StringPiece sp)
+    : fullArgString(sp),
+      fill(kDefaultFill),
+      align(Align::DEFAULT),
+      sign(Sign::DEFAULT),
+      basePrefix(false),
+      thousandsSeparator(false),
+      trailingDot(false),
+      width(kDefaultWidth),
+      widthIndex(kNoIndex),
+      precision(kDefaultPrecision),
+      presentation(kDefaultPresentation),
+      nextKeyMode_(NextKeyMode::NONE) {
+    if (!sp.empty()) {
+      initSlow();
+    }
+  }
+
+  enum class Type {
+    INTEGER,
+    FLOAT,
+    OTHER
+  };
+  /**
+   * Validate the argument for the given type; throws on error.
+   */
+  void validate(Type type) const;
+
+  /**
+   * Throw an exception if the first argument is false.  The exception
+   * message will contain the argument string as well as any passed-in
+   * arguments to enforce, formatted using folly::to<std::string>.
+   */
+  template <typename... Args>
+  void enforce(bool v, Args&&... args) const {
+    if (UNLIKELY(!v)) {
+      error(std::forward<Args>(args)...);
+    }
+  }
+
+  template <typename... Args>
+  std::string errorStr(Args&&... args) const;
+  template <typename... Args>
+  FOLLY_NORETURN void error(Args&&... args) const;
+
+  /**
+   * Full argument string, as passed in to the constructor.
+   */
+  StringPiece fullArgString;
+
+  /**
+   * Fill
+   */
+  static constexpr char kDefaultFill = '\0';
+  char fill;
+
+  /**
+   * Alignment
+   */
+  enum class Align : uint8_t {
+    DEFAULT,
+    LEFT,
+    RIGHT,
+    PAD_AFTER_SIGN,
+    CENTER,
+    INVALID
+  };
+  Align align;
+
+  /**
+   * Sign
+   */
+  enum class Sign : uint8_t {
+    DEFAULT,
+    PLUS_OR_MINUS,
+    MINUS,
+    SPACE_OR_MINUS,
+    INVALID
+  };
+  Sign sign;
+
+  /**
+   * Output base prefix (0 for octal, 0x for hex)
+   */
+  bool basePrefix;
+
+  /**
+   * Output thousands separator (comma)
+   */
+  bool thousandsSeparator;
+
+  /**
+   * Force a trailing decimal on doubles which could be rendered as ints
+   */
+  bool trailingDot;
+
+  /**
+   * Field width and optional argument index
+   */
+  static constexpr int kDefaultWidth = -1;
+  static constexpr int kDynamicWidth = -2;
+  static constexpr int kNoIndex = -1;
+  int width;
+  int widthIndex;
+
+  /**
+   * Precision
+   */
+  static constexpr int kDefaultPrecision = -1;
+  int precision;
+
+  /**
+   * Presentation
+   */
+  static constexpr char kDefaultPresentation = '\0';
+  char presentation;
+
+  /**
+   * Split a key component from "key", which must be non-empty (an exception
+   * is thrown otherwise).
+   */
+  template <bool emptyOk=false>
+  StringPiece splitKey();
+
+  /**
+   * Is the entire key empty?
+   */
+  bool keyEmpty() const {
+    return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
+  }
+
+  /**
+   * Split an key component from "key", which must be non-empty and a valid
+   * integer (an exception is thrown otherwise).
+   */
+  int splitIntKey();
+
+  void setNextIntKey(int val) {
+    assert(nextKeyMode_ == NextKeyMode::NONE);
+    nextKeyMode_ = NextKeyMode::INT;
+    nextIntKey_ = val;
+  }
+
+  void setNextKey(StringPiece val) {
+    assert(nextKeyMode_ == NextKeyMode::NONE);
+    nextKeyMode_ = NextKeyMode::STRING;
+    nextKey_ = val;
+  }
+
+ private:
+  void initSlow();
+  template <bool emptyOk>
+  StringPiece doSplitKey();
+
+  StringPiece key_;
+  int nextIntKey_;
+  StringPiece nextKey_;
+  enum class NextKeyMode {
+    NONE,
+    INT,
+    STRING,
+  };
+  NextKeyMode nextKeyMode_;
+};
+
+template <typename... Args>
+inline std::string FormatArg::errorStr(Args&&... args) const {
+  return to<std::string>(
+    "invalid format argument {", fullArgString, "}: ",
+    std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+inline void FormatArg::error(Args&&... args) const {
+  throw BadFormatArg(errorStr(std::forward<Args>(args)...));
+}
+
+template <bool emptyOk>
+inline StringPiece FormatArg::splitKey() {
+  enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
+  return doSplitKey<emptyOk>();
+}
+
+template <bool emptyOk>
+inline StringPiece FormatArg::doSplitKey() {
+  if (nextKeyMode_ == NextKeyMode::STRING) {
+    nextKeyMode_ = NextKeyMode::NONE;
+    if (!emptyOk) {  // static
+      enforce(!nextKey_.empty(), "non-empty key required");
+    }
+    return nextKey_;
+  }
+
+  if (key_.empty()) {
+    if (!emptyOk) {  // static
+      error("non-empty key required");
+    }
+    return StringPiece();
+  }
+
+  const char* b = key_.begin();
+  const char* e = key_.end();
+  const char* p;
+  if (e[-1] == ']') {
+    --e;
+    p = static_cast<const char*>(memchr(b, '[', e - b));
+    enforce(p, "unmatched ']'");
+  } else {
+    p = static_cast<const char*>(memchr(b, '.', e - b));
+  }
+  if (p) {
+    key_.assign(p + 1, e);
+  } else {
+    p = e;
+    key_.clear();
+  }
+  if (!emptyOk) {  // static
+    enforce(b != p, "non-empty key required");
+  }
+  return StringPiece(b, p);
+}
+
+inline int FormatArg::splitIntKey() {
+  if (nextKeyMode_ == NextKeyMode::INT) {
+    nextKeyMode_ = NextKeyMode::NONE;
+    return nextIntKey_;
+  }
+  try {
+    return to<int>(doSplitKey<true>());
+  } catch (const std::out_of_range& e) {
+    error("integer key required");
+    return 0;  // unreached
+  }
+}
+
+}  // namespace folly
+
+#endif /* FOLLY_FORMATARG_H_ */
diff --git a/faux-folly/folly/FormatTraits.h b/faux-folly/folly/FormatTraits.h
new file mode 100644
index 0000000..7ca2ae5
--- /dev/null
+++ b/faux-folly/folly/FormatTraits.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_FORMAT_TRAITS_H_
+#define FOLLY_FORMAT_TRAITS_H_
+
+#include <type_traits>
+
+namespace folly { namespace detail {
+
+// Shortcut, so we don't have to use enable_if everywhere
+struct FormatTraitsBase {
+  typedef void enabled;
+};
+
+// Traits that define enabled, value_type, and at() for anything
+// indexable with integral keys: pointers, arrays, vectors, and maps
+// with integral keys
+template <class T, class Enable = void> struct IndexableTraits;
+
+// Base class for sequences (vectors, deques)
+template <class C>
+struct IndexableTraitsSeq : public FormatTraitsBase {
+  typedef C container_type;
+  typedef typename C::value_type value_type;
+
+  static const value_type& at(const C& c, int idx) {
+    return c.at(idx);
+  }
+
+  static const value_type& at(const C& c, int idx, const value_type& dflt) {
+    return (idx >= 0 && size_t(idx) < c.size()) ? c.at(idx) : dflt;
+  }
+};
+
+// Base class for associative types (maps)
+template <class C>
+struct IndexableTraitsAssoc : public FormatTraitsBase {
+  typedef typename C::value_type::second_type value_type;
+
+  static const value_type& at(const C& c, int idx) {
+    return c.at(static_cast<typename C::key_type>(idx));
+  }
+
+  static const value_type& at(const C& c, int idx, const value_type& dflt) {
+    auto pos = c.find(static_cast<typename C::key_type>(idx));
+    return pos != c.end() ? pos->second : dflt;
+  }
+};
+
+}}  // namespaces
+
+#endif /* FOLLY_FORMAT_TRAITS_H_ */
diff --git a/faux-folly/folly/Hash.h b/faux-folly/folly/Hash.h
new file mode 100644
index 0000000..99d7dae
--- /dev/null
+++ b/faux-folly/folly/Hash.h
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BASE_HASH_H_
+#define FOLLY_BASE_HASH_H_
+
+#include <cstring>
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <tuple>
+
+#include <folly/ApplyTuple.h>
+#include <folly/SpookyHashV1.h>
+#include <folly/SpookyHashV2.h>
+
+/*
+ * Various hashing functions.
+ */
+
+namespace folly { namespace hash {
+
+// This is a general-purpose way to create a single hash from multiple
+// hashable objects. hash_combine_generic takes a class Hasher implementing
+// hash<T>; hash_combine uses a default hasher StdHasher that uses std::hash.
+// hash_combine_generic hashes each argument and combines those hashes in
+// an order-dependent way to yield a new hash.
+
+
+// This is the Hash128to64 function from Google's cityhash (available
+// under the MIT License).  We use it to reduce multiple 64 bit hashes
+// into a single hash.
+inline uint64_t hash_128_to_64(const uint64_t upper, const uint64_t lower) {
+  // Murmur-inspired hashing.
+  const uint64_t kMul = 0x9ddfea08eb382d69ULL;
+  uint64_t a = (lower ^ upper) * kMul;
+  a ^= (a >> 47);
+  uint64_t b = (upper ^ a) * kMul;
+  b ^= (b >> 47);
+  b *= kMul;
+  return b;
+}
+
+// Never used, but gcc demands it.
+template <class Hasher>
+inline size_t hash_combine_generic() {
+  return 0;
+}
+
+template <
+    class Iter,
+    class Hash = std::hash<typename std::iterator_traits<Iter>::value_type>>
+uint64_t hash_range(Iter begin,
+                    Iter end,
+                    uint64_t hash = 0,
+                    Hash hasher = Hash()) {
+  for (; begin != end; ++begin) {
+    hash = hash_128_to_64(hash, hasher(*begin));
+  }
+  return hash;
+}
+
+template <class Hasher, typename T, typename... Ts>
+size_t hash_combine_generic(const T& t, const Ts&... ts) {
+  size_t seed = Hasher::hash(t);
+  if (sizeof...(ts) == 0) {
+    return seed;
+  }
+  size_t remainder = hash_combine_generic<Hasher>(ts...);
+  return hash_128_to_64(seed, remainder);
+}
+
+// Simply uses std::hash to hash.  Note that std::hash is not guaranteed
+// to be a very good hash function; provided std::hash doesn't collide on
+// the individual inputs, you are fine, but that won't be true for, say,
+// strings or pairs
+class StdHasher {
+ public:
+  template <typename T>
+  static size_t hash(const T& t) {
+    return std::hash<T>()(t);
+  }
+};
+
+template <typename T, typename... Ts>
+size_t hash_combine(const T& t, const Ts&... ts) {
+  return hash_combine_generic<StdHasher>(t, ts...);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+/*
+ * Thomas Wang 64 bit mix hash function
+ */
+
+inline uint64_t twang_mix64(uint64_t key) {
+  key = (~key) + (key << 21);  // key *= (1 << 21) - 1; key -= 1;
+  key = key ^ (key >> 24);
+  key = key + (key << 3) + (key << 8);  // key *= 1 + (1 << 3) + (1 << 8)
+  key = key ^ (key >> 14);
+  key = key + (key << 2) + (key << 4);  // key *= 1 + (1 << 2) + (1 << 4)
+  key = key ^ (key >> 28);
+  key = key + (key << 31);  // key *= 1 + (1 << 31)
+  return key;
+}
+
+/*
+ * Inverse of twang_mix64
+ *
+ * Note that twang_unmix64 is significantly slower than twang_mix64.
+ */
+
+inline uint64_t twang_unmix64(uint64_t key) {
+  // See the comments in jenkins_rev_unmix32 for an explanation as to how this
+  // was generated
+  key *= 4611686016279904257U;
+  key ^= (key >> 28) ^ (key >> 56);
+  key *= 14933078535860113213U;
+  key ^= (key >> 14) ^ (key >> 28) ^ (key >> 42) ^ (key >> 56);
+  key *= 15244667743933553977U;
+  key ^= (key >> 24) ^ (key >> 48);
+  key = (key + 1) * 9223367638806167551U;
+  return key;
+}
+
+/*
+ * Thomas Wang downscaling hash function
+ */
+
+inline uint32_t twang_32from64(uint64_t key) {
+  key = (~key) + (key << 18);
+  key = key ^ (key >> 31);
+  key = key * 21;
+  key = key ^ (key >> 11);
+  key = key + (key << 6);
+  key = key ^ (key >> 22);
+  return (uint32_t) key;
+}
+
+/*
+ * Robert Jenkins' reversible 32 bit mix hash function
+ */
+
+inline uint32_t jenkins_rev_mix32(uint32_t key) {
+  key += (key << 12);  // key *= (1 + (1 << 12))
+  key ^= (key >> 22);
+  key += (key << 4);   // key *= (1 + (1 << 4))
+  key ^= (key >> 9);
+  key += (key << 10);  // key *= (1 + (1 << 10))
+  key ^= (key >> 2);
+  // key *= (1 + (1 << 7)) * (1 + (1 << 12))
+  key += (key << 7);
+  key += (key << 12);
+  return key;
+}
+
+/*
+ * Inverse of jenkins_rev_mix32
+ *
+ * Note that jenkinks_rev_unmix32 is significantly slower than
+ * jenkins_rev_mix32.
+ */
+
+inline uint32_t jenkins_rev_unmix32(uint32_t key) {
+  // These are the modular multiplicative inverses (in Z_2^32) of the
+  // multiplication factors in jenkins_rev_mix32, in reverse order.  They were
+  // computed using the Extended Euclidean algorithm, see
+  // http://en.wikipedia.org/wiki/Modular_multiplicative_inverse
+  key *= 2364026753U;
+
+  // The inverse of a ^= (a >> n) is
+  // b = a
+  // for (int i = n; i < 32; i += n) {
+  //   b ^= (a >> i);
+  // }
+  key ^=
+    (key >> 2) ^ (key >> 4) ^ (key >> 6) ^ (key >> 8) ^
+    (key >> 10) ^ (key >> 12) ^ (key >> 14) ^ (key >> 16) ^
+    (key >> 18) ^ (key >> 20) ^ (key >> 22) ^ (key >> 24) ^
+    (key >> 26) ^ (key >> 28) ^ (key >> 30);
+  key *= 3222273025U;
+  key ^= (key >> 9) ^ (key >> 18) ^ (key >> 27);
+  key *= 4042322161U;
+  key ^= (key >> 22);
+  key *= 16773121U;
+  return key;
+}
+
+/*
+ * Fowler / Noll / Vo (FNV) Hash
+ *     http://www.isthe.com/chongo/tech/comp/fnv/
+ */
+
+const uint32_t FNV_32_HASH_START = 2166136261UL;
+const uint64_t FNV_64_HASH_START = 14695981039346656037ULL;
+
+inline uint32_t fnv32(const char* s,
+                      uint32_t hash = FNV_32_HASH_START) {
+  for (; *s; ++s) {
+    hash += (hash << 1) + (hash << 4) + (hash << 7) +
+            (hash << 8) + (hash << 24);
+    hash ^= static_cast<signed char>(*s);
+  }
+  return hash;
+}
+
+inline uint32_t fnv32_buf(const void* buf,
+                          size_t n,
+                          uint32_t hash = FNV_32_HASH_START) {
+  const char* char_buf = reinterpret_cast<const char*>(buf);
+
+  for (size_t i = 0; i < n; ++i) {
+    hash += (hash << 1) + (hash << 4) + (hash << 7) +
+            (hash << 8) + (hash << 24);
+    hash ^= static_cast<signed char>(char_buf[i]);
+  }
+
+  return hash;
+}
+
+inline uint32_t fnv32(const std::string& str,
+                      uint32_t hash = FNV_32_HASH_START) {
+  return fnv32_buf(str.data(), str.size(), hash);
+}
+
+inline uint64_t fnv64(const char* s,
+                      uint64_t hash = FNV_64_HASH_START) {
+  for (; *s; ++s) {
+    hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) +
+      (hash << 8) + (hash << 40);
+    hash ^= static_cast<signed char>(*s);
+  }
+  return hash;
+}
+
+inline uint64_t fnv64_buf(const void* buf,
+                          size_t n,
+                          uint64_t hash = FNV_64_HASH_START) {
+  const signed char* char_buf = reinterpret_cast<const signed char*>(buf);
+
+  for (size_t i = 0; i < n; ++i) {
+    hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) +
+      (hash << 8) + (hash << 40);
+    hash ^= static_cast<signed char>(char_buf[i]);
+  }
+  return hash;
+}
+
+inline uint64_t fnv64(const std::string& str,
+                      uint64_t hash = FNV_64_HASH_START) {
+  return fnv64_buf(str.data(), str.size(), hash);
+}
+
+/*
+ * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html
+ */
+
+#define get16bits(d) (*((const uint16_t*) (d)))
+
+inline uint32_t hsieh_hash32_buf(const void* buf, size_t len) {
+  const char* s = reinterpret_cast<const char*>(buf);
+  uint32_t hash = static_cast<uint32_t>(len);
+  uint32_t tmp;
+  size_t rem;
+
+  if (len <= 0 || buf == 0) {
+    return 0;
+  }
+
+  rem = len & 3;
+  len >>= 2;
+
+  /* Main loop */
+  for (;len > 0; len--) {
+    hash  += get16bits (s);
+    tmp    = (get16bits (s+2) << 11) ^ hash;
+    hash   = (hash << 16) ^ tmp;
+    s  += 2*sizeof (uint16_t);
+    hash  += hash >> 11;
+  }
+
+  /* Handle end cases */
+  switch (rem) {
+  case 3:
+    hash += get16bits(s);
+    hash ^= hash << 16;
+    hash ^= s[sizeof (uint16_t)] << 18;
+    hash += hash >> 11;
+    break;
+  case 2:
+    hash += get16bits(s);
+    hash ^= hash << 11;
+    hash += hash >> 17;
+    break;
+  case 1:
+    hash += *s;
+    hash ^= hash << 10;
+    hash += hash >> 1;
+  }
+
+  /* Force "avalanching" of final 127 bits */
+  hash ^= hash << 3;
+  hash += hash >> 5;
+  hash ^= hash << 4;
+  hash += hash >> 17;
+  hash ^= hash << 25;
+  hash += hash >> 6;
+
+  return hash;
+};
+
+#undef get16bits
+
+inline uint32_t hsieh_hash32(const char* s) {
+  return hsieh_hash32_buf(s, std::strlen(s));
+}
+
+inline uint32_t hsieh_hash32_str(const std::string& str) {
+  return hsieh_hash32_buf(str.data(), str.size());
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace hash
+
+template<class Key, class Enable = void>
+struct hasher;
+
+struct Hash {
+  template <class T>
+  size_t operator()(const T& v) const {
+    return hasher<T>()(v);
+  }
+
+  template <class T, class... Ts>
+  size_t operator()(const T& t, const Ts&... ts) const {
+    return hash::hash_128_to_64((*this)(t), (*this)(ts...));
+  }
+};
+
+template<> struct hasher<int32_t> {
+  size_t operator()(int32_t key) const {
+    return hash::jenkins_rev_mix32(uint32_t(key));
+  }
+};
+
+template<> struct hasher<uint32_t> {
+  size_t operator()(uint32_t key) const {
+    return hash::jenkins_rev_mix32(key);
+  }
+};
+
+template<> struct hasher<int64_t> {
+  size_t operator()(int64_t key) const {
+    return hash::twang_mix64(uint64_t(key));
+  }
+};
+
+template<> struct hasher<uint64_t> {
+  size_t operator()(uint64_t key) const {
+    return hash::twang_mix64(key);
+  }
+};
+
+template <class T>
+struct hasher<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
+  size_t operator()(T key) const {
+    return Hash()(static_cast<typename std::underlying_type<T>::type>(key));
+  }
+};
+
+template <class T1, class T2>
+struct hasher<std::pair<T1, T2>> {
+  size_t operator()(const std::pair<T1, T2>& key) const {
+    return Hash()(key.first, key.second);
+  }
+};
+
+template <typename... Ts>
+struct hasher<std::tuple<Ts...>> {
+  size_t operator() (const std::tuple<Ts...>& key) const {
+    return applyTuple(Hash(), key);
+  }
+};
+
+// recursion
+template <size_t index, typename... Ts>
+struct TupleHasher {
+  size_t operator()(std::tuple<Ts...> const& key) const {
+    return hash::hash_combine(
+      TupleHasher<index - 1, Ts...>()(key),
+      std::get<index>(key));
+  }
+};
+
+// base
+template <typename... Ts>
+struct TupleHasher<0, Ts...> {
+  size_t operator()(std::tuple<Ts...> const& key) const {
+    // we could do std::hash here directly, but hash_combine hides all the
+    // ugly templating implicitly
+    return hash::hash_combine(std::get<0>(key));
+  }
+};
+
+} // namespace folly
+
+// Custom hash functions.
+namespace std {
+  // Hash function for pairs. Requires default hash functions for both
+  // items in the pair.
+  template <typename T1, typename T2>
+  struct hash<std::pair<T1, T2> > {
+  public:
+    size_t operator()(const std::pair<T1, T2>& x) const {
+      return folly::hash::hash_combine(x.first, x.second);
+    }
+  };
+
+  // Hash function for tuples. Requires default hash functions for all types.
+  template <typename... Ts>
+  struct hash<std::tuple<Ts...>> {
+    size_t operator()(std::tuple<Ts...> const& key) const {
+      folly::TupleHasher<
+        std::tuple_size<std::tuple<Ts...>>::value - 1, // start index
+        Ts...> hasher;
+
+      return hasher(key);
+    }
+  };
+} // namespace std
+
+#endif
diff --git a/faux-folly/folly/IndexedMemPool.h b/faux-folly/folly/IndexedMemPool.h
new file mode 100644
index 0000000..23b42ac
--- /dev/null
+++ b/faux-folly/folly/IndexedMemPool.h
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_INDEXEDMEMPOOL_H
+#define FOLLY_INDEXEDMEMPOOL_H
+
+#include <type_traits>
+#include <stdint.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <boost/noncopyable.hpp>
+#include <folly/AtomicStruct.h>
+#include <folly/detail/CacheLocality.h>
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
+namespace folly {
+
+namespace detail {
+template <typename Pool>
+struct IndexedMemPoolRecycler;
+}
+
+/// Instances of IndexedMemPool dynamically allocate and then pool their
+/// element type (T), returning 4-byte integer indices that can be passed
+/// to the pool's operator[] method to access or obtain pointers to the
+/// actual elements.  The memory backing items returned from the pool
+/// will always be readable, even if items have been returned to the pool.
+/// These two features are useful for lock-free algorithms.  The indexing
+/// behavior makes it easy to build tagged pointer-like-things, since
+/// a large number of elements can be managed using fewer bits than a
+/// full pointer.  The access-after-free behavior makes it safe to read
+/// from T-s even after they have been recycled, since it is guaranteed
+/// that the memory won't have been returned to the OS and unmapped
+/// (the algorithm must still use a mechanism to validate that the read
+/// was correct, but it doesn't have to worry about page faults), and if
+/// the elements use internal sequence numbers it can be guaranteed that
+/// there won't be an ABA match due to the element being overwritten with
+/// a different type that has the same bit pattern.
+///
+/// IndexedMemPool has two object lifecycle strategies.  The first
+/// is to construct objects when they are allocated from the pool and
+/// destroy them when they are recycled.  In this mode allocIndex and
+/// allocElem have emplace-like semantics.  In the second mode, objects
+/// are default-constructed the first time they are removed from the pool,
+/// and deleted when the pool itself is deleted.  By default the first
+/// mode is used for non-trivial T, and the second is used for trivial T.
+///
+/// IMPORTANT: Space for extra elements is allocated to account for those
+/// that are inaccessible because they are in other local lists, so the
+/// actual number of items that can be allocated ranges from capacity to
+/// capacity + (NumLocalLists_-1)*LocalListLimit_.  This is important if
+/// you are trying to maximize the capacity of the pool while constraining
+/// the bit size of the resulting pointers, because the pointers will
+/// actually range up to the boosted capacity.  See maxIndexForCapacity
+/// and capacityForMaxIndex.
+///
+/// To avoid contention, NumLocalLists_ free lists of limited (less than
+/// or equal to LocalListLimit_) size are maintained, and each thread
+/// retrieves and returns entries from its associated local list.  If the
+/// local list becomes too large then elements are placed in bulk in a
+/// global free list.  This allows items to be efficiently recirculated
+/// from consumers to producers.  AccessSpreader is used to access the
+/// local lists, so there is no performance advantage to having more
+/// local lists than L1 caches.
+///
+/// The pool mmap-s the entire necessary address space when the pool is
+/// constructed, but delays element construction.  This means that only
+/// elements that are actually returned to the caller get paged into the
+/// process's resident set (RSS).
+template <typename T,
+          int NumLocalLists_ = 32,
+          int LocalListLimit_ = 200,
+          template<typename> class Atom = std::atomic,
+          bool EagerRecycleWhenTrivial = false,
+          bool EagerRecycleWhenNotTrivial = true>
+struct IndexedMemPool : boost::noncopyable {
+  typedef T value_type;
+
+  typedef std::unique_ptr<T, detail::IndexedMemPoolRecycler<IndexedMemPool>>
+      UniquePtr;
+
+  static_assert(LocalListLimit_ <= 255, "LocalListLimit must fit in 8 bits");
+  enum {
+    NumLocalLists = NumLocalLists_,
+    LocalListLimit = LocalListLimit_
+  };
+
+
+  static constexpr bool eagerRecycle() {
+    return std::is_trivial<T>::value
+        ? EagerRecycleWhenTrivial : EagerRecycleWhenNotTrivial;
+  }
+
+  // these are public because clients may need to reason about the number
+  // of bits required to hold indices from a pool, given its capacity
+
+  static constexpr uint32_t maxIndexForCapacity(uint32_t capacity) {
+    // index of uint32_t(-1) == UINT32_MAX is reserved for isAllocated tracking
+    return std::min(uint64_t(capacity) + (NumLocalLists - 1) * LocalListLimit,
+                    uint64_t(uint32_t(-1) - 1));
+  }
+
+  static constexpr uint32_t capacityForMaxIndex(uint32_t maxIndex) {
+    return maxIndex - (NumLocalLists - 1) * LocalListLimit;
+  }
+
+
+  /// Constructs a pool that can allocate at least _capacity_ elements,
+  /// even if all the local lists are full
+  explicit IndexedMemPool(uint32_t capacity)
+    : actualCapacity_(maxIndexForCapacity(capacity))
+    , size_(0)
+    , globalHead_(TaggedPtr{})
+  {
+    const size_t needed = sizeof(Slot) * (actualCapacity_ + 1);
+    long pagesize = sysconf(_SC_PAGESIZE);
+    mmapLength_ = ((needed - 1) & ~(pagesize - 1)) + pagesize;
+    assert(needed <= mmapLength_ && mmapLength_ < needed + pagesize);
+    assert((mmapLength_ % pagesize) == 0);
+
+    slots_ = static_cast<Slot*>(mmap(nullptr, mmapLength_,
+                                     PROT_READ | PROT_WRITE,
+                                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+    if (slots_ == MAP_FAILED) {
+      assert(errno == ENOMEM);
+      throw std::bad_alloc();
+    }
+  }
+
+  /// Destroys all of the contained elements
+  ~IndexedMemPool() {
+    if (!eagerRecycle()) {
+      for (size_t i = size_; i > 0; --i) {
+        slots_[i].~Slot();
+      }
+    }
+    munmap(slots_, mmapLength_);
+  }
+
+  /// Returns a lower bound on the number of elements that may be
+  /// simultaneously allocated and not yet recycled.  Because of the
+  /// local lists it is possible that more elements than this are returned
+  /// successfully
+  size_t capacity() {
+    return capacityForMaxIndex(actualCapacity_);
+  }
+
+  /// Finds a slot with a non-zero index, emplaces a T there if we're
+  /// using the eager recycle lifecycle mode, and returns the index,
+  /// or returns 0 if no elements are available.
+  template <typename ...Args>
+  uint32_t allocIndex(Args&&... args) {
+    static_assert(sizeof...(Args) == 0 || eagerRecycle(),
+        "emplace-style allocation requires eager recycle, "
+        "which is defaulted only for non-trivial types");
+    auto idx = localPop(localHead());
+    if (idx != 0 && eagerRecycle()) {
+      T* ptr = &slot(idx).elem;
+      new (ptr) T(std::forward<Args>(args)...);
+    }
+    return idx;
+  }
+
+  /// If an element is available, returns a std::unique_ptr to it that will
+  /// recycle the element to the pool when it is reclaimed, otherwise returns
+  /// a null (falsy) std::unique_ptr
+  template <typename ...Args>
+  UniquePtr allocElem(Args&&... args) {
+    auto idx = allocIndex(std::forward<Args>(args)...);
+    T* ptr = idx == 0 ? nullptr : &slot(idx).elem;
+    return UniquePtr(ptr, typename UniquePtr::deleter_type(this));
+  }
+
+  /// Gives up ownership previously granted by alloc()
+  void recycleIndex(uint32_t idx) {
+    assert(isAllocated(idx));
+    if (eagerRecycle()) {
+      slot(idx).elem.~T();
+    }
+    localPush(localHead(), idx);
+  }
+
+  /// Provides access to the pooled element referenced by idx
+  T& operator[](uint32_t idx) {
+    return slot(idx).elem;
+  }
+
+  /// Provides access to the pooled element referenced by idx
+  const T& operator[](uint32_t idx) const {
+    return slot(idx).elem;
+  }
+
+  /// If elem == &pool[idx], then pool.locateElem(elem) == idx.  Also,
+  /// pool.locateElem(nullptr) == 0
+  uint32_t locateElem(const T* elem) const {
+    if (!elem) {
+      return 0;
+    }
+
+    static_assert(std::is_standard_layout<Slot>::value, "offsetof needs POD");
+
+    auto slot = reinterpret_cast<const Slot*>(
+        reinterpret_cast<const char*>(elem) - offsetof(Slot, elem));
+    auto rv = slot - slots_;
+
+    // this assert also tests that rv is in range
+    assert(elem == &(*this)[rv]);
+    return rv;
+  }
+
+  /// Returns true iff idx has been alloc()ed and not recycleIndex()ed
+  bool isAllocated(uint32_t idx) const {
+    return slot(idx).localNext == uint32_t(-1);
+  }
+
+
+ private:
+  ///////////// types
+
+  struct Slot {
+    T elem;
+    uint32_t localNext;
+    uint32_t globalNext;
+
+    Slot() : localNext{}, globalNext{} {}
+  };
+
+  struct TaggedPtr {
+    uint32_t idx;
+
+    // size is bottom 8 bits, tag in top 24.  g++'s code generation for
+    // bitfields seems to depend on the phase of the moon, plus we can
+    // do better because we can rely on other checks to avoid masking
+    uint32_t tagAndSize;
+
+    enum : uint32_t {
+        SizeBits = 8,
+        SizeMask = (1U << SizeBits) - 1,
+        TagIncr = 1U << SizeBits,
+    };
+
+    uint32_t size() const {
+      return tagAndSize & SizeMask;
+    }
+
+    TaggedPtr withSize(uint32_t repl) const {
+      assert(repl <= LocalListLimit);
+      return TaggedPtr{ idx, (tagAndSize & ~SizeMask) | repl };
+    }
+
+    TaggedPtr withSizeIncr() const {
+      assert(size() < LocalListLimit);
+      return TaggedPtr{ idx, tagAndSize + 1 };
+    }
+
+    TaggedPtr withSizeDecr() const {
+      assert(size() > 0);
+      return TaggedPtr{ idx, tagAndSize - 1 };
+    }
+
+    TaggedPtr withIdx(uint32_t repl) const {
+      return TaggedPtr{ repl, tagAndSize + TagIncr };
+    }
+
+    TaggedPtr withEmpty() const {
+      return withIdx(0).withSize(0);
+    }
+  };
+
+  struct FOLLY_ALIGN_TO_AVOID_FALSE_SHARING LocalList {
+    AtomicStruct<TaggedPtr,Atom> head;
+
+    LocalList() : head(TaggedPtr{}) {}
+  };
+
+  ////////// fields
+
+  /// the actual number of slots that we will allocate, to guarantee
+  /// that we will satisfy the capacity requested at construction time.
+  /// They will be numbered 1..actualCapacity_ (note the 1-based counting),
+  /// and occupy slots_[1..actualCapacity_].
+  size_t actualCapacity_;
+
+  /// the number of bytes allocated from mmap, which is a multiple of
+  /// the page size of the machine
+  size_t mmapLength_;
+
+  /// this records the number of slots that have actually been constructed.
+  /// To allow use of atomic ++ instead of CAS, we let this overflow.
+  /// The actual number of constructed elements is min(actualCapacity_,
+  /// size_)
+  Atom<uint32_t> size_;
+
+  /// raw storage, only 1..min(size_,actualCapacity_) (inclusive) are
+  /// actually constructed.  Note that slots_[0] is not constructed or used
+  FOLLY_ALIGN_TO_AVOID_FALSE_SHARING Slot* slots_;
+
+  /// use AccessSpreader to find your list.  We use stripes instead of
+  /// thread-local to avoid the need to grow or shrink on thread start
+  /// or join.   These are heads of lists chained with localNext
+  LocalList local_[NumLocalLists];
+
+  /// this is the head of a list of node chained by globalNext, that are
+  /// themselves each the head of a list chained by localNext
+  FOLLY_ALIGN_TO_AVOID_FALSE_SHARING AtomicStruct<TaggedPtr,Atom> globalHead_;
+
+  ///////////// private methods
+
+  size_t slotIndex(uint32_t idx) const {
+    assert(0 < idx &&
+           idx <= actualCapacity_ &&
+           idx <= size_.load(std::memory_order_acquire));
+    return idx;
+  }
+
+  Slot& slot(uint32_t idx) {
+    return slots_[slotIndex(idx)];
+  }
+
+  const Slot& slot(uint32_t idx) const {
+    return slots_[slotIndex(idx)];
+  }
+
+  // localHead references a full list chained by localNext.  s should
+  // reference slot(localHead), it is passed as a micro-optimization
+  void globalPush(Slot& s, uint32_t localHead) {
+    while (true) {
+      TaggedPtr gh = globalHead_.load(std::memory_order_acquire);
+      s.globalNext = gh.idx;
+      if (globalHead_.compare_exchange_strong(gh, gh.withIdx(localHead))) {
+        // success
+        return;
+      }
+    }
+  }
+
+  // idx references a single node
+  void localPush(AtomicStruct<TaggedPtr,Atom>& head, uint32_t idx) {
+    Slot& s = slot(idx);
+    TaggedPtr h = head.load(std::memory_order_acquire);
+    while (true) {
+      s.localNext = h.idx;
+
+      if (h.size() == LocalListLimit) {
+        // push will overflow local list, steal it instead
+        if (head.compare_exchange_strong(h, h.withEmpty())) {
+          // steal was successful, put everything in the global list
+          globalPush(s, idx);
+          return;
+        }
+      } else {
+        // local list has space
+        if (head.compare_exchange_strong(h, h.withIdx(idx).withSizeIncr())) {
+          // success
+          return;
+        }
+      }
+      // h was updated by failing CAS
+    }
+  }
+
+  // returns 0 if empty
+  uint32_t globalPop() {
+    while (true) {
+      TaggedPtr gh = globalHead_.load(std::memory_order_acquire);
+      if (gh.idx == 0 || globalHead_.compare_exchange_strong(
+                  gh, gh.withIdx(slot(gh.idx).globalNext))) {
+        // global list is empty, or pop was successful
+        return gh.idx;
+      }
+    }
+  }
+
+  // returns 0 if allocation failed
+  uint32_t localPop(AtomicStruct<TaggedPtr,Atom>& head) {
+    while (true) {
+      TaggedPtr h = head.load(std::memory_order_acquire);
+      if (h.idx != 0) {
+        // local list is non-empty, try to pop
+        Slot& s = slot(h.idx);
+        if (head.compare_exchange_strong(
+                    h, h.withIdx(s.localNext).withSizeDecr())) {
+          // success
+          s.localNext = uint32_t(-1);
+          return h.idx;
+        }
+        continue;
+      }
+
+      uint32_t idx = globalPop();
+      if (idx == 0) {
+        // global list is empty, allocate and construct new slot
+        if (size_.load(std::memory_order_relaxed) >= actualCapacity_ ||
+            (idx = ++size_) > actualCapacity_) {
+          // allocation failed
+          return 0;
+        }
+        // default-construct it now if we aren't going to construct and
+        // destroy on each allocation
+        if (!eagerRecycle()) {
+          T* ptr = &slot(idx).elem;
+          new (ptr) T();
+        }
+        slot(idx).localNext = uint32_t(-1);
+        return idx;
+      }
+
+      Slot& s = slot(idx);
+      if (head.compare_exchange_strong(
+                  h, h.withIdx(s.localNext).withSize(LocalListLimit))) {
+        // global list moved to local list, keep head for us
+        s.localNext = uint32_t(-1);
+        return idx;
+      }
+      // local bulk push failed, return idx to the global list and try again
+      globalPush(s, idx);
+    }
+  }
+
+  AtomicStruct<TaggedPtr,Atom>& localHead() {
+    auto stripe = detail::AccessSpreader<Atom>::current(NumLocalLists);
+    return local_[stripe].head;
+  }
+};
+
+namespace detail {
+
+/// This is a stateful Deleter functor, which allows std::unique_ptr
+/// to track elements allocated from an IndexedMemPool by tracking the
+/// associated pool.  See IndexedMemPool::allocElem.
+template <typename Pool>
+struct IndexedMemPoolRecycler {
+  Pool* pool;
+
+  explicit IndexedMemPoolRecycler(Pool* pool) : pool(pool) {}
+
+  IndexedMemPoolRecycler(const IndexedMemPoolRecycler<Pool>& rhs)
+      = default;
+  IndexedMemPoolRecycler& operator= (const IndexedMemPoolRecycler<Pool>& rhs)
+      = default;
+
+  void operator()(typename Pool::value_type* elem) const {
+    pool->recycleIndex(pool->locateElem(elem));
+  }
+};
+
+}
+
+} // namespace folly
+
+# pragma GCC diagnostic pop
+#endif
diff --git a/faux-folly/folly/Lazy.h b/faux-folly/folly/Lazy.h
new file mode 100644
index 0000000..bca89d9
--- /dev/null
+++ b/faux-folly/folly/Lazy.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2015 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.
+ */
+#ifndef FOLLY_LAZY_H_
+#define FOLLY_LAZY_H_
+
+#include <utility>
+#include <type_traits>
+
+#include <folly/Optional.h>
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+/*
+ * Lazy -- for delayed initialization of a value.  The value's
+ * initialization will be computed on demand at its first use, but
+ * will not be recomputed if its value is requested again.  The value
+ * may still be mutated after its initialization if the lazy is not
+ * declared const.
+ *
+ * The value is created using folly::lazy, usually with a lambda, and
+ * its value is requested using operator().
+ *
+ * Note that the value is not safe for concurrent accesses by multiple
+ * threads, even if you declare it const.  See note below.
+ *
+ *
+ * Example Usage:
+ *
+ *   void foo() {
+ *     auto const val = folly::lazy([&]{
+ *       return something_expensive(blah());
+ *     });
+ *
+ *     if (condition1) {
+ *       use(val());
+ *     }
+ *     if (condition2) {
+ *       useMaybeAgain(val());
+ *     } else {
+ *       // Unneeded in this branch.
+ *     }
+ *   }
+ *
+ *
+ * Rationale:
+ *
+ *    - operator() is used to request the value instead of an implicit
+ *      conversion because the slight syntactic overhead in common
+ *      seems worth the increased clarity.
+ *
+ *    - Lazy values do not model CopyConstructible because it is
+ *      unclear what semantics would be desirable.  Either copies
+ *      should share the cached value (adding overhead to cases that
+ *      don't need to support copies), or they could recompute the
+ *      value unnecessarily.  Sharing with mutable lazies would also
+ *      leave them with non-value semantics despite looking
+ *      value-like.
+ *
+ *    - Not thread safe for const accesses.  Many use cases for lazy
+ *      values are local variables on the stack, where multiple
+ *      threads shouldn't even be able to reach the value.  It still
+ *      is useful to indicate/check that the value doesn't change with
+ *      const, particularly when it is captured by a large family of
+ *      lambdas.  Adding internal synchronization seems like it would
+ *      pessimize the most common use case in favor of less likely use
+ *      cases.
+ *
+ */
+
+//////////////////////////////////////////////////////////////////////
+
+namespace detail {
+
+template<class Func>
+struct Lazy {
+  typedef typename std::result_of<Func()>::type result_type;
+
+  explicit Lazy(Func&& f) : func_(std::move(f)) {}
+  explicit Lazy(Func& f)  : func_(f) {}
+
+  Lazy(Lazy&& o)
+    : value_(std::move(o.value_))
+    , func_(std::move(o.func_))
+  {}
+
+  Lazy(const Lazy&) = delete;
+  Lazy& operator=(const Lazy&) = delete;
+  Lazy& operator=(Lazy&&) = delete;
+
+  const result_type& operator()() const {
+    return const_cast<Lazy&>(*this)();
+  }
+
+  result_type& operator()() {
+    if (!value_) value_ = func_();
+    return *value_;
+  }
+
+private:
+  Optional<result_type> value_;
+  Func func_;
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+template<class Func>
+detail::Lazy<typename std::remove_reference<Func>::type>
+lazy(Func&& fun) {
+  return detail::Lazy<typename std::remove_reference<Func>::type>(
+    std::forward<Func>(fun)
+  );
+}
+
+//////////////////////////////////////////////////////////////////////
+
+}
+
+#endif
diff --git a/faux-folly/folly/LifoSem.cpp b/faux-folly/folly/LifoSem.cpp
new file mode 100644
index 0000000..eab911d
--- /dev/null
+++ b/faux-folly/folly/LifoSem.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 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/LifoSem.h>
+
+/// Raw node storage is preallocated in a contiguous memory segment,
+/// but we use an anonymous mmap so the physical memory used (RSS) will
+/// only reflect the maximum number of waiters that actually existed
+/// concurrently.  For blocked threads the max node count is limited by the
+/// number of threads, so we can conservatively estimate that this will be
+/// < 10k.  For LifoEventSem, however, we could potentially have many more.
+///
+/// On a 64-bit architecture each LifoSemRawNode takes 16 bytes.  We make
+/// the pool 1 million entries.
+
+LIFOSEM_DECLARE_POOL(std::atomic, 1000000)
+
+namespace folly {
+
+ShutdownSemError::ShutdownSemError(const std::string& msg)
+  : std::runtime_error(msg)
+{}
+
+ShutdownSemError::~ShutdownSemError() noexcept {
+}
+
+}
diff --git a/faux-folly/folly/LifoSem.h b/faux-folly/folly/LifoSem.h
new file mode 100644
index 0000000..24b37c8
--- /dev/null
+++ b/faux-folly/folly/LifoSem.h
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_LIFOSEM_H
+#define FOLLY_LIFOSEM_H
+
+#include <string.h>
+#include <stdint.h>
+#include <atomic>
+#include <algorithm>
+#include <memory>
+#include <system_error>
+
+#include <folly/AtomicStruct.h>
+#include <folly/Baton.h>
+#include <folly/IndexedMemPool.h>
+#include <folly/Likely.h>
+#include <folly/detail/CacheLocality.h>
+
+namespace folly {
+
+template <template<typename> class Atom = std::atomic,
+          class BatonType = Baton<Atom>>
+struct LifoSemImpl;
+
+/// LifoSem is a semaphore that wakes its waiters in a manner intended to
+/// maximize performance rather than fairness.  It should be preferred
+/// to a mutex+condvar or POSIX sem_t solution when all of the waiters
+/// are equivalent.  It is faster than a condvar or sem_t, and it has a
+/// shutdown state that might save you a lot of complexity when it comes
+/// time to shut down your work pipelines.  LifoSem is larger than sem_t,
+/// but that is only because it uses padding and alignment to avoid
+/// false sharing.
+///
+/// LifoSem allows multi-post and multi-tryWait, and provides a shutdown
+/// state that awakens all waiters.  LifoSem is faster than sem_t because
+/// it performs exact wakeups, so it often requires fewer system calls.
+/// It provides all of the functionality of sem_t except for timed waiting.
+/// It is called LifoSem because its wakeup policy is approximately LIFO,
+/// rather than the usual FIFO.
+///
+/// The core semaphore operations provided are:
+///
+/// -- post() -- if there is a pending waiter, wake it up, otherwise
+/// increment the value of the semaphore.  If the value of the semaphore
+/// is already 2^32-1, does nothing.  Compare to sem_post().
+///
+/// -- post(n) -- equivalent to n calls to post(), but much more efficient.
+/// sem_t has no equivalent to this method.
+///
+/// -- bool tryWait() -- if the semaphore's value is positive, decrements it
+/// and returns true, otherwise returns false.  Compare to sem_trywait().
+///
+/// -- uint32_t tryWait(uint32_t n) -- attempts to decrement the semaphore's
+/// value by n, returning the amount by which it actually was decremented
+/// (a value from 0 to n inclusive).  Not atomic.  Equivalent to n calls
+/// to tryWait().  sem_t has no equivalent to this method.
+///
+/// -- wait() -- waits until tryWait() can succeed.  Compare to sem_wait().
+///
+/// LifoSem also has the notion of a shutdown state, in which any calls
+/// that would block (or are already blocked) throw ShutdownSemError.
+/// Note the difference between a call to wait() and a call to wait()
+/// that might block.  In the former case tryWait() would succeed, and no
+/// isShutdown() check is performed.  In the latter case an exception is
+/// thrown.  This behavior allows a LifoSem controlling work distribution
+/// to drain.  If you want to immediately stop all waiting on shutdown,
+/// you can just check isShutdown() yourself (preferrably wrapped in
+/// an UNLIKELY).  This fast-stop behavior is easy to add, but difficult
+/// to remove if you want the draining behavior, which is why we have
+/// chosen the former.  Since wait() is the only method that can block,
+/// it is the only one that is affected by the shutdown state.
+///
+/// All LifoSem operations operations except valueGuess() are guaranteed
+/// to be linearizable.
+typedef LifoSemImpl<> LifoSem;
+
+
+/// The exception thrown when wait()ing on an isShutdown() LifoSem
+struct ShutdownSemError : public std::runtime_error {
+  explicit ShutdownSemError(const std::string& msg);
+  virtual ~ShutdownSemError() noexcept;
+};
+
+namespace detail {
+
+// Internally, a LifoSem is either a value or a linked list of wait nodes.
+// This union is captured in the LifoSemHead type, which holds either a
+// value or an indexed pointer to the list.  LifoSemHead itself is a value
+// type, the head is a mutable atomic box containing a LifoSemHead value.
+// Each wait node corresponds to exactly one waiter.  Values can flow
+// through the semaphore either by going into and out of the head's value,
+// or by direct communication from a poster to a waiter.  The former path
+// is taken when there are no pending waiters, the latter otherwise.  The
+// general flow of a post is to try to increment the value or pop-and-post
+// a wait node.  Either of those have the effect of conveying one semaphore
+// unit.  Waiting is the opposite, either a decrement of the value or
+// push-and-wait of a wait node.  The generic LifoSemBase abstracts the
+// actual mechanism by which a wait node's post->wait communication is
+// performed, which is why we have LifoSemRawNode and LifoSemNode.
+
+/// LifoSemRawNode is the actual pooled storage that backs LifoSemNode
+/// for user-specified Handoff types.  This is done so that we can have
+/// a large static IndexedMemPool of nodes, instead of per-type pools
+template <template<typename> class Atom>
+struct LifoSemRawNode {
+  std::aligned_storage<sizeof(void*),alignof(void*)>::type raw;
+
+  /// The IndexedMemPool index of the next node in this chain, or 0
+  /// if none.  This will be set to uint32_t(-1) if the node is being
+  /// posted due to a shutdown-induced wakeup
+  uint32_t next;
+
+  bool isShutdownNotice() const { return next == uint32_t(-1); }
+  void clearShutdownNotice() { next = 0; }
+  void setShutdownNotice() { next = uint32_t(-1); }
+
+  typedef folly::IndexedMemPool<LifoSemRawNode<Atom>,32,200,Atom> Pool;
+
+  /// Storage for all of the waiter nodes for LifoSem-s that use Atom
+  static Pool pool;
+};
+
+/// Use this macro to declare the static storage that backs the raw nodes
+/// for the specified atomic type
+#define LIFOSEM_DECLARE_POOL(Atom, capacity)                       \
+    template<>                                                     \
+    folly::detail::LifoSemRawNode<Atom>::Pool                      \
+        folly::detail::LifoSemRawNode<Atom>::pool((capacity));
+
+/// Handoff is a type not bigger than a void* that knows how to perform a
+/// single post() -> wait() communication.  It must have a post() method.
+/// If it has a wait() method then LifoSemBase's wait() implementation
+/// will work out of the box, otherwise you will need to specialize
+/// LifoSemBase::wait accordingly.
+template <typename Handoff, template<typename> class Atom>
+struct LifoSemNode : public LifoSemRawNode<Atom> {
+
+  static_assert(sizeof(Handoff) <= sizeof(LifoSemRawNode<Atom>::raw),
+      "Handoff too big for small-object optimization, use indirection");
+  static_assert(alignof(Handoff) <=
+                alignof(decltype(LifoSemRawNode<Atom>::raw)),
+      "Handoff alignment constraint not satisfied");
+
+  template <typename ...Args>
+  void init(Args&&... args) {
+    new (&this->raw) Handoff(std::forward<Args>(args)...);
+  }
+
+  void destroy() {
+    handoff().~Handoff();
+#ifndef NDEBUG
+    memset(&this->raw, 'F', sizeof(this->raw));
+#endif
+  }
+
+  Handoff& handoff() {
+    return *static_cast<Handoff*>(static_cast<void*>(&this->raw));
+  }
+
+  const Handoff& handoff() const {
+    return *static_cast<const Handoff*>(static_cast<const void*>(&this->raw));
+  }
+};
+
+template <typename Handoff, template<typename> class Atom>
+struct LifoSemNodeRecycler {
+  void operator()(LifoSemNode<Handoff,Atom>* elem) const {
+    elem->destroy();
+    auto idx = LifoSemRawNode<Atom>::pool.locateElem(elem);
+    LifoSemRawNode<Atom>::pool.recycleIndex(idx);
+  }
+};
+
+/// LifoSemHead is a 64-bit struct that holds a 32-bit value, some state
+/// bits, and a sequence number used to avoid ABA problems in the lock-free
+/// management of the LifoSem's wait lists.  The value can either hold
+/// an integral semaphore value (if there are no waiters) or a node index
+/// (see IndexedMemPool) for the head of a list of wait nodes
+class LifoSemHead {
+  // What we really want are bitfields:
+  //  uint64_t data : 32; uint64_t isNodeIdx : 1; uint64_t seq : 31;
+  // Unfortunately g++ generates pretty bad code for this sometimes (I saw
+  // -O3 code from gcc 4.7.1 copying the bitfields one at a time instead of
+  // in bulk, for example).  We can generate better code anyway by assuming
+  // that setters won't be given values that cause under/overflow, and
+  // putting the sequence at the end where its planned overflow doesn't
+  // need any masking.
+  //
+  // data == 0 (empty list) with isNodeIdx is conceptually the same
+  // as data == 0 (no unclaimed increments) with !isNodeIdx, we always
+  // convert the former into the latter to make the logic simpler.
+  enum {
+    IsNodeIdxShift = 32,
+    IsShutdownShift = 33,
+    SeqShift = 34,
+  };
+  enum : uint64_t {
+    IsNodeIdxMask = uint64_t(1) << IsNodeIdxShift,
+    IsShutdownMask = uint64_t(1) << IsShutdownShift,
+    SeqIncr = uint64_t(1) << SeqShift,
+    SeqMask = ~(SeqIncr - 1),
+  };
+
+ public:
+
+  uint64_t bits;
+
+  //////// getters
+
+  inline uint32_t idx() const {
+    assert(isNodeIdx());
+    assert(uint32_t(bits) != 0);
+    return uint32_t(bits);
+  }
+  inline uint32_t value() const {
+    assert(!isNodeIdx());
+    return uint32_t(bits);
+  }
+  inline constexpr bool isNodeIdx() const {
+    return (bits & IsNodeIdxMask) != 0;
+  }
+  inline constexpr bool isShutdown() const {
+    return (bits & IsShutdownMask) != 0;
+  }
+  inline constexpr uint32_t seq() const {
+    return uint32_t(bits >> SeqShift);
+  }
+
+  //////// setter-like things return a new struct
+
+  /// This should only be used for initial construction, not for setting
+  /// the value, because it clears the sequence number
+  static inline constexpr LifoSemHead fresh(uint32_t value) {
+    return LifoSemHead{ value };
+  }
+
+  /// Returns the LifoSemHead that results from popping a waiter node,
+  /// given the current waiter node's next ptr
+  inline LifoSemHead withPop(uint32_t idxNext) const {
+    assert(isNodeIdx());
+    if (idxNext == 0) {
+      // no isNodeIdx bit or data bits.  Wraparound of seq bits is okay
+      return LifoSemHead{ (bits & (SeqMask | IsShutdownMask)) + SeqIncr };
+    } else {
+      // preserve sequence bits (incremented with wraparound okay) and
+      // isNodeIdx bit, replace all data bits
+      return LifoSemHead{
+          (bits & (SeqMask | IsShutdownMask | IsNodeIdxMask)) +
+          SeqIncr + idxNext };
+    }
+  }
+
+  /// Returns the LifoSemHead that results from pushing a new waiter node
+  inline LifoSemHead withPush(uint32_t _idx) const {
+    assert(isNodeIdx() || value() == 0);
+    assert(!isShutdown());
+    assert(_idx != 0);
+    return LifoSemHead{ (bits & SeqMask) | IsNodeIdxMask | _idx };
+  }
+
+  /// Returns the LifoSemHead with value increased by delta, with
+  /// saturation if the maximum value is reached
+  inline LifoSemHead withValueIncr(uint32_t delta) const {
+    assert(!isNodeIdx());
+    auto rv = LifoSemHead{ bits + SeqIncr + delta };
+    if (UNLIKELY(rv.isNodeIdx())) {
+      // value has overflowed into the isNodeIdx bit
+      rv = LifoSemHead{ (rv.bits & ~IsNodeIdxMask) | (IsNodeIdxMask - 1) };
+    }
+    return rv;
+  }
+
+  /// Returns the LifoSemHead that results from decrementing the value
+  inline LifoSemHead withValueDecr(uint32_t delta) const {
+    assert(delta > 0 && delta <= value());
+    return LifoSemHead{ bits + SeqIncr - delta };
+  }
+
+  /// Returns the LifoSemHead with the same state as the current node,
+  /// but with the shutdown bit set
+  inline LifoSemHead withShutdown() const {
+    return LifoSemHead{ bits | IsShutdownMask };
+  }
+
+  inline constexpr bool operator== (const LifoSemHead& rhs) const {
+    return bits == rhs.bits;
+  }
+  inline constexpr bool operator!= (const LifoSemHead& rhs) const {
+    return !(*this == rhs);
+  }
+};
+
+/// LifoSemBase is the engine for several different types of LIFO
+/// semaphore.  LifoSemBase handles storage of positive semaphore values
+/// and wait nodes, but the actual waiting and notification mechanism is
+/// up to the client.
+///
+/// The Handoff type is responsible for arranging one wakeup notification.
+/// See LifoSemNode for more information on how to make your own.
+template <typename Handoff,
+          template<typename> class Atom = std::atomic>
+struct LifoSemBase : boost::noncopyable {
+
+  /// Constructor
+  explicit LifoSemBase(uint32_t initialValue = 0)
+    : head_(LifoSemHead::fresh(initialValue)) {}
+
+  /// Silently saturates if value is already 2^32-1
+  void post() {
+    auto idx = incrOrPop(1);
+    if (idx != 0) {
+      idxToNode(idx).handoff().post();
+    }
+  }
+
+  /// Equivalent to n calls to post(), except may be much more efficient.
+  /// At any point in time at which the semaphore's value would exceed
+  /// 2^32-1 if tracked with infinite precision, it may be silently
+  /// truncated to 2^32-1.  This saturation is not guaranteed to be exact,
+  /// although it is guaranteed that overflow won't result in wrap-around.
+  /// There would be a substantial performance and complexity cost in
+  /// guaranteeing exact saturation (similar to the cost of maintaining
+  /// linearizability near the zero value, but without as much of
+  /// a benefit).
+  void post(uint32_t n) {
+    uint32_t idx;
+    while (n > 0 && (idx = incrOrPop(n)) != 0) {
+      // pop accounts for only 1
+      idxToNode(idx).handoff().post();
+      --n;
+    }
+  }
+
+  /// Returns true iff shutdown() has been called
+  bool isShutdown() const {
+    return UNLIKELY(head_.load(std::memory_order_acquire).isShutdown());
+  }
+
+  /// Prevents blocking on this semaphore, causing all blocking wait()
+  /// calls to throw ShutdownSemError.  Both currently blocked wait() and
+  /// future calls to wait() for which tryWait() would return false will
+  /// cause an exception.  Calls to wait() for which the matching post()
+  /// has already occurred will proceed normally.
+  void shutdown() {
+    // first set the shutdown bit
+    auto h = head_.load(std::memory_order_acquire);
+    while (!h.isShutdown()) {
+      if (head_.compare_exchange_strong(h, h.withShutdown())) {
+        // success
+        h = h.withShutdown();
+        break;
+      }
+      // compare_exchange_strong rereads h, retry
+    }
+
+    // now wake up any waiters
+    while (h.isNodeIdx()) {
+      auto& node = idxToNode(h.idx());
+      auto repl = h.withPop(node.next);
+      if (head_.compare_exchange_strong(h, repl)) {
+        // successful pop, wake up the waiter and move on.  The next
+        // field is used to convey that this wakeup didn't consume a value
+        node.setShutdownNotice();
+        node.handoff().post();
+        h = repl;
+      }
+    }
+  }
+
+  /// Returns true iff value was decremented
+  bool tryWait() {
+    uint32_t n = 1;
+    auto rv = decrOrPush(n, 0);
+    assert((rv == WaitResult::DECR && n == 0) ||
+           (rv != WaitResult::DECR && n == 1));
+    // SHUTDOWN is okay here, since we don't actually wait
+    return rv == WaitResult::DECR;
+  }
+
+  /// Equivalent to (but may be much more efficient than) n calls to
+  /// tryWait().  Returns the total amount by which the semaphore's value
+  /// was decreased
+  uint32_t tryWait(uint32_t n) {
+    auto const orig = n;
+    while (n > 0) {
+#ifndef NDEBUG
+      auto prev = n;
+#endif
+      auto rv = decrOrPush(n, 0);
+      assert((rv == WaitResult::DECR && n < prev) ||
+             (rv != WaitResult::DECR && n == prev));
+      if (rv != WaitResult::DECR) {
+        break;
+      }
+    }
+    return orig - n;
+  }
+
+  /// Blocks the current thread until there is a matching post or the
+  /// semaphore is shut down.  Throws ShutdownSemError if the semaphore
+  /// has been shut down and this method would otherwise be blocking.
+  /// Note that wait() doesn't throw during shutdown if tryWait() would
+  /// return true
+  void wait() {
+    // early check isn't required for correctness, but is an important
+    // perf win if we can avoid allocating and deallocating a node
+    if (tryWait()) {
+      return;
+    }
+
+    // allocateNode() won't compile unless Handoff has a default
+    // constructor
+    UniquePtr node = allocateNode();
+
+    auto rv = tryWaitOrPush(*node);
+    if (UNLIKELY(rv == WaitResult::SHUTDOWN)) {
+      assert(isShutdown());
+      throw ShutdownSemError("wait() would block but semaphore is shut down");
+    }
+
+    if (rv == WaitResult::PUSH) {
+      node->handoff().wait();
+      if (UNLIKELY(node->isShutdownNotice())) {
+        // this wait() didn't consume a value, it was triggered by shutdown
+        assert(isShutdown());
+        throw ShutdownSemError(
+            "blocking wait() interrupted by semaphore shutdown");
+      }
+
+      // node->handoff().wait() can't return until after the node has
+      // been popped and post()ed, so it is okay for the UniquePtr to
+      // recycle the node now
+    }
+    // else node wasn't pushed, so it is safe to recycle
+  }
+
+  /// Returns a guess at the current value, designed for debugging.
+  /// If there are no concurrent posters or waiters then this will
+  /// be correct
+  uint32_t valueGuess() const {
+    // this is actually linearizable, but we don't promise that because
+    // we may want to add striping in the future to help under heavy
+    // contention
+    auto h = head_.load(std::memory_order_acquire);
+    return h.isNodeIdx() ? 0 : h.value();
+  }
+
+ protected:
+
+  enum class WaitResult {
+    PUSH,
+    DECR,
+    SHUTDOWN,
+  };
+
+  /// The type of a std::unique_ptr that will automatically return a
+  /// LifoSemNode to the appropriate IndexedMemPool
+  typedef std::unique_ptr<LifoSemNode<Handoff, Atom>,
+                          LifoSemNodeRecycler<Handoff, Atom>> UniquePtr;
+
+  /// Returns a node that can be passed to decrOrLink
+  template <typename... Args>
+  UniquePtr allocateNode(Args&&... args) {
+    auto idx = LifoSemRawNode<Atom>::pool.allocIndex();
+    if (idx != 0) {
+      auto& node = idxToNode(idx);
+      node.clearShutdownNotice();
+      try {
+        node.init(std::forward<Args>(args)...);
+      } catch (...) {
+        LifoSemRawNode<Atom>::pool.recycleIndex(idx);
+        throw;
+      }
+      return UniquePtr(&node);
+    } else {
+      return UniquePtr();
+    }
+  }
+
+  /// Returns DECR if the semaphore value was decremented (and waiterNode
+  /// was untouched), PUSH if a reference to the wait node was pushed,
+  /// or SHUTDOWN if decrement was not possible and push wasn't allowed
+  /// because isShutdown().  Ownership of the wait node remains the
+  /// responsibility of the caller, who must not release it until after
+  /// the node's Handoff has been posted.
+  WaitResult tryWaitOrPush(LifoSemNode<Handoff, Atom>& waiterNode) {
+    uint32_t n = 1;
+    return decrOrPush(n, nodeToIdx(waiterNode));
+  }
+
+ private:
+
+  FOLLY_ALIGN_TO_AVOID_FALSE_SHARING
+  folly::AtomicStruct<LifoSemHead,Atom> head_;
+
+  char padding_[folly::detail::CacheLocality::kFalseSharingRange -
+      sizeof(LifoSemHead)];
+
+
+  static LifoSemNode<Handoff, Atom>& idxToNode(uint32_t idx) {
+    auto raw = &LifoSemRawNode<Atom>::pool[idx];
+    return *static_cast<LifoSemNode<Handoff, Atom>*>(raw);
+  }
+
+  static uint32_t nodeToIdx(const LifoSemNode<Handoff, Atom>& node) {
+    return LifoSemRawNode<Atom>::pool.locateElem(&node);
+  }
+
+  /// Either increments by n and returns 0, or pops a node and returns it.
+  /// If n + the stripe's value overflows, then the stripe's value
+  /// saturates silently at 2^32-1
+  uint32_t incrOrPop(uint32_t n) {
+    while (true) {
+      assert(n > 0);
+
+      auto head = head_.load(std::memory_order_acquire);
+      if (head.isNodeIdx()) {
+        auto& node = idxToNode(head.idx());
+        if (head_.compare_exchange_strong(head, head.withPop(node.next))) {
+          // successful pop
+          return head.idx();
+        }
+      } else {
+        auto after = head.withValueIncr(n);
+        if (head_.compare_exchange_strong(head, after)) {
+          // successful incr
+          return 0;
+        }
+      }
+      // retry
+    }
+  }
+
+  /// Returns DECR if some amount was decremented, with that amount
+  /// subtracted from n.  If n is 1 and this function returns DECR then n
+  /// must be 0 afterward.  Returns PUSH if no value could be decremented
+  /// and idx was pushed, or if idx was zero and no push was performed but
+  /// a push would have been performed with a valid node.  Returns SHUTDOWN
+  /// if the caller should have blocked but isShutdown().  If idx == 0,
+  /// may return PUSH even after isShutdown() or may return SHUTDOWN
+  WaitResult decrOrPush(uint32_t& n, uint32_t idx) {
+    assert(n > 0);
+
+    while (true) {
+      auto head = head_.load(std::memory_order_acquire);
+
+      if (!head.isNodeIdx() && head.value() > 0) {
+        // decr
+        auto delta = std::min(n, head.value());
+        if (head_.compare_exchange_strong(head, head.withValueDecr(delta))) {
+          n -= delta;
+          return WaitResult::DECR;
+        }
+      } else {
+        // push
+        if (idx == 0) {
+          return WaitResult::PUSH;
+        }
+
+        if (UNLIKELY(head.isShutdown())) {
+          return WaitResult::SHUTDOWN;
+        }
+
+        auto& node = idxToNode(idx);
+        node.next = head.isNodeIdx() ? head.idx() : 0;
+        if (head_.compare_exchange_strong(head, head.withPush(idx))) {
+          // push succeeded
+          return WaitResult::PUSH;
+        }
+      }
+    }
+    // retry
+  }
+};
+
+} // namespace detail
+
+template <template<typename> class Atom, class BatonType>
+struct LifoSemImpl : public detail::LifoSemBase<BatonType, Atom> {
+  explicit LifoSemImpl(uint32_t v = 0)
+    : detail::LifoSemBase<BatonType, Atom>(v) {}
+};
+
+} // namespace folly
+
+#endif
diff --git a/faux-folly/folly/Likely.h b/faux-folly/folly/Likely.h
new file mode 100644
index 0000000..1286736
--- /dev/null
+++ b/faux-folly/folly/Likely.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Compiler hints to indicate the fast path of an "if" branch: whether
+ * the if condition is likely to be true or false.
+ *
+ * @author Tudor Bosman (tudorb@fb.com)
+ */
+
+#ifndef FOLLY_BASE_LIKELY_H_
+#define FOLLY_BASE_LIKELY_H_
+
+#undef LIKELY
+#undef UNLIKELY
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define LIKELY(x)   (__builtin_expect((x), 1))
+#define UNLIKELY(x) (__builtin_expect((x), 0))
+#else
+#define LIKELY(x)   (x)
+#define UNLIKELY(x) (x)
+#endif
+
+#endif /* FOLLY_BASE_LIKELY_H_ */
diff --git a/faux-folly/folly/Logging.h b/faux-folly/folly/Logging.h
new file mode 100644
index 0000000..d5a3173
--- /dev/null
+++ b/faux-folly/folly/Logging.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_LOGGING_H_
+#define FOLLY_LOGGING_H_
+
+#include <atomic>
+#include <chrono>
+#include <glog/logging.h>
+
+#ifndef FB_LOG_EVERY_MS
+/**
+ * Issues a LOG(severity) no more often than every
+ * milliseconds. Example:
+ *
+ * FB_LOG_EVERY_MS(INFO, 10000) << "At least ten seconds passed"
+ *   " since you last saw this.";
+ *
+ * The implementation uses for statements to introduce variables in
+ * a nice way that doesn't mess surrounding statements.  It is thread
+ * safe.  Non-positive intervals will always log.
+ */
+#define FB_LOG_EVERY_MS(severity, milli_interval)                            \
+  for (decltype(milli_interval) FB_LEM_once = 1,                             \
+                                FB_LEM_interval = (milli_interval);          \
+       FB_LEM_once; )                                                        \
+    for (::std::chrono::milliseconds::rep FB_LEM_prev, FB_LEM_now =          \
+             FB_LEM_interval <= 0 ? 0 :                                      \
+             ::std::chrono::duration_cast< ::std::chrono::milliseconds>(     \
+                 ::std::chrono::system_clock::now().time_since_epoch()       \
+                 ).count();                                                  \
+         FB_LEM_once; )                                                      \
+      for (static ::std::atomic< ::std::chrono::milliseconds::rep>           \
+               FB_LEM_hist; FB_LEM_once; FB_LEM_once = 0)                    \
+        if (FB_LEM_interval > 0 &&                                           \
+            (FB_LEM_now - (FB_LEM_prev =                                     \
+                           FB_LEM_hist.load(std::memory_order_acquire)) <    \
+                                                          FB_LEM_interval || \
+             !FB_LEM_hist.compare_exchange_strong(FB_LEM_prev,FB_LEM_now))) {\
+        } else                                                               \
+          LOG(severity)
+
+#endif
+
+#endif  // FOLLY_LOGGING_H_
diff --git a/faux-folly/folly/MPMCQueue.h b/faux-folly/folly/MPMCQueue.h
new file mode 100644
index 0000000..a268195
--- /dev/null
+++ b/faux-folly/folly/MPMCQueue.h
@@ -0,0 +1,717 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <assert.h>
+#include <boost/noncopyable.hpp>
+#include <limits>
+#include <string.h>
+#include <type_traits>
+#include <unistd.h>
+
+#include <folly/Traits.h>
+#include <folly/detail/CacheLocality.h>
+#include <folly/detail/TurnSequencer.h>
+
+namespace folly {
+
+namespace detail {
+
+template<typename T, template<typename> class Atom>
+struct SingleElementQueue;
+
+template <typename T> class MPMCPipelineStageImpl;
+
+} // namespace detail
+
+/// MPMCQueue<T> is a high-performance bounded concurrent queue that
+/// supports multiple producers, multiple consumers, and optional blocking.
+/// The queue has a fixed capacity, for which all memory will be allocated
+/// up front.  The bulk of the work of enqueuing and dequeuing can be
+/// performed in parallel.
+///
+/// MPMCQueue is linearizable.  That means that if a call to write(A)
+/// returns before a call to write(B) begins, then A will definitely end up
+/// in the queue before B, and if a call to read(X) returns before a call
+/// to read(Y) is started, that X will be something from earlier in the
+/// queue than Y.  This also means that if a read call returns a value, you
+/// can be sure that all previous elements of the queue have been assigned
+/// a reader (that reader might not yet have returned, but it exists).
+///
+/// The underlying implementation uses a ticket dispenser for the head and
+/// the tail, spreading accesses across N single-element queues to produce
+/// a queue with capacity N.  The ticket dispensers use atomic increment,
+/// which is more robust to contention than a CAS loop.  Each of the
+/// single-element queues uses its own CAS to serialize access, with an
+/// adaptive spin cutoff.  When spinning fails on a single-element queue
+/// it uses futex()'s _BITSET operations to reduce unnecessary wakeups
+/// even if multiple waiters are present on an individual queue (such as
+/// when the MPMCQueue's capacity is smaller than the number of enqueuers
+/// or dequeuers).
+///
+/// In benchmarks (contained in tao/queues/ConcurrentQueueTests)
+/// it handles 1 to 1, 1 to N, N to 1, and N to M thread counts better
+/// than any of the alternatives present in fbcode, for both small (~10)
+/// and large capacities.  In these benchmarks it is also faster than
+/// tbb::concurrent_bounded_queue for all configurations.  When there are
+/// many more threads than cores, MPMCQueue is _much_ faster than the tbb
+/// queue because it uses futex() to block and unblock waiting threads,
+/// rather than spinning with sched_yield.
+///
+/// NOEXCEPT INTERACTION: tl;dr; If it compiles you're fine.  Ticket-based
+/// queues separate the assignment of queue positions from the actual
+/// construction of the in-queue elements, which means that the T
+/// constructor used during enqueue must not throw an exception.  This is
+/// enforced at compile time using type traits, which requires that T be
+/// adorned with accurate noexcept information.  If your type does not
+/// use noexcept, you will have to wrap it in something that provides
+/// the guarantee.  We provide an alternate safe implementation for types
+/// that don't use noexcept but that are marked folly::IsRelocatable
+/// and boost::has_nothrow_constructor, which is common for folly types.
+/// In particular, if you can declare FOLLY_ASSUME_FBVECTOR_COMPATIBLE
+/// then your type can be put in MPMCQueue.
+///
+/// If you have a pool of N queue consumers that you want to shut down
+/// after the queue has drained, one way is to enqueue N sentinel values
+/// to the queue.  If the producer doesn't know how many consumers there
+/// are you can enqueue one sentinel and then have each consumer requeue
+/// two sentinels after it receives it (by requeuing 2 the shutdown can
+/// complete in O(log P) time instead of O(P)).
+template<typename T,
+         template<typename> class Atom = std::atomic>
+class MPMCQueue : boost::noncopyable {
+
+  static_assert(std::is_nothrow_constructible<T,T&&>::value ||
+                folly::IsRelocatable<T>::value,
+      "T must be relocatable or have a noexcept move constructor");
+
+  friend class detail::MPMCPipelineStageImpl<T>;
+ public:
+  typedef T value_type;
+
+  explicit MPMCQueue(size_t queueCapacity)
+    : capacity_(queueCapacity)
+    , pushTicket_(0)
+    , popTicket_(0)
+    , pushSpinCutoff_(0)
+    , popSpinCutoff_(0)
+  {
+    if (queueCapacity == 0)
+      throw std::invalid_argument(
+        "MPMCQueue with explicit capacity 0 is impossible"
+      );
+
+    // would sigfpe if capacity is 0
+    stride_ = computeStride(queueCapacity);
+    slots_ = new detail::SingleElementQueue<T,Atom>[queueCapacity +
+                                                    2 * kSlotPadding];
+
+    // ideally this would be a static assert, but g++ doesn't allow it
+    assert(alignof(MPMCQueue<T,Atom>)
+           >= detail::CacheLocality::kFalseSharingRange);
+    assert(static_cast<uint8_t*>(static_cast<void*>(&popTicket_))
+           - static_cast<uint8_t*>(static_cast<void*>(&pushTicket_))
+           >= detail::CacheLocality::kFalseSharingRange);
+  }
+
+  /// A default-constructed queue is useful because a usable (non-zero
+  /// capacity) queue can be moved onto it or swapped with it
+  MPMCQueue() noexcept
+    : capacity_(0)
+    , slots_(nullptr)
+    , stride_(0)
+    , pushTicket_(0)
+    , popTicket_(0)
+    , pushSpinCutoff_(0)
+    , popSpinCutoff_(0)
+  {}
+
+  /// IMPORTANT: The move constructor is here to make it easier to perform
+  /// the initialization phase, it is not safe to use when there are any
+  /// concurrent accesses (this is not checked).
+  MPMCQueue(MPMCQueue<T,Atom>&& rhs) noexcept
+    : capacity_(rhs.capacity_)
+    , slots_(rhs.slots_)
+    , stride_(rhs.stride_)
+    , pushTicket_(rhs.pushTicket_.load(std::memory_order_relaxed))
+    , popTicket_(rhs.popTicket_.load(std::memory_order_relaxed))
+    , pushSpinCutoff_(rhs.pushSpinCutoff_.load(std::memory_order_relaxed))
+    , popSpinCutoff_(rhs.popSpinCutoff_.load(std::memory_order_relaxed))
+  {
+    // relaxed ops are okay for the previous reads, since rhs queue can't
+    // be in concurrent use
+
+    // zero out rhs
+    rhs.capacity_ = 0;
+    rhs.slots_ = nullptr;
+    rhs.stride_ = 0;
+    rhs.pushTicket_.store(0, std::memory_order_relaxed);
+    rhs.popTicket_.store(0, std::memory_order_relaxed);
+    rhs.pushSpinCutoff_.store(0, std::memory_order_relaxed);
+    rhs.popSpinCutoff_.store(0, std::memory_order_relaxed);
+  }
+
+  /// IMPORTANT: The move operator is here to make it easier to perform
+  /// the initialization phase, it is not safe to use when there are any
+  /// concurrent accesses (this is not checked).
+  MPMCQueue<T,Atom> const& operator= (MPMCQueue<T,Atom>&& rhs) {
+    if (this != &rhs) {
+      this->~MPMCQueue();
+      new (this) MPMCQueue(std::move(rhs));
+    }
+    return *this;
+  }
+
+  /// MPMCQueue can only be safely destroyed when there are no
+  /// pending enqueuers or dequeuers (this is not checked).
+  ~MPMCQueue() {
+    delete[] slots_;
+  }
+
+  /// Returns the number of successful reads minus the number of successful
+  /// writes.  Waiting blockingRead and blockingWrite calls are included,
+  /// so this value can be negative.
+  ssize_t size() const noexcept {
+    // since both pushes and pops increase monotonically, we can get a
+    // consistent snapshot either by bracketing a read of popTicket_ with
+    // two reads of pushTicket_ that return the same value, or the other
+    // way around.  We maximize our chances by alternately attempting
+    // both bracketings.
+    uint64_t pushes = pushTicket_.load(std::memory_order_acquire); // A
+    uint64_t pops = popTicket_.load(std::memory_order_acquire); // B
+    while (true) {
+      uint64_t nextPushes = pushTicket_.load(std::memory_order_acquire); // C
+      if (pushes == nextPushes) {
+        // pushTicket_ didn't change from A (or the previous C) to C,
+        // so we can linearize at B (or D)
+        return pushes - pops;
+      }
+      pushes = nextPushes;
+      uint64_t nextPops = popTicket_.load(std::memory_order_acquire); // D
+      if (pops == nextPops) {
+        // popTicket_ didn't chance from B (or the previous D), so we
+        // can linearize at C
+        return pushes - pops;
+      }
+      pops = nextPops;
+    }
+  }
+
+  /// Returns true if there are no items available for dequeue
+  bool isEmpty() const noexcept {
+    return size() <= 0;
+  }
+
+  /// Returns true if there is currently no empty space to enqueue
+  bool isFull() const noexcept {
+    // careful with signed -> unsigned promotion, since size can be negative
+    return size() >= static_cast<ssize_t>(capacity_);
+  }
+
+  /// Returns is a guess at size() for contexts that don't need a precise
+  /// value, such as stats.
+  ssize_t sizeGuess() const noexcept {
+    return writeCount() - readCount();
+  }
+
+  /// Doesn't change
+  size_t capacity() const noexcept {
+    return capacity_;
+  }
+
+  /// Returns the total number of calls to blockingWrite or successful
+  /// calls to write, including those blockingWrite calls that are
+  /// currently blocking
+  uint64_t writeCount() const noexcept {
+    return pushTicket_.load(std::memory_order_acquire);
+  }
+
+  /// Returns the total number of calls to blockingRead or successful
+  /// calls to read, including those blockingRead calls that are currently
+  /// blocking
+  uint64_t readCount() const noexcept {
+    return popTicket_.load(std::memory_order_acquire);
+  }
+
+  /// Enqueues a T constructed from args, blocking until space is
+  /// available.  Note that this method signature allows enqueue via
+  /// move, if args is a T rvalue, via copy, if args is a T lvalue, or
+  /// via emplacement if args is an initializer list that can be passed
+  /// to a T constructor.
+  template <typename ...Args>
+  void blockingWrite(Args&&... args) noexcept {
+    enqueueWithTicket(pushTicket_++, std::forward<Args>(args)...);
+  }
+
+  /// If an item can be enqueued with no blocking, does so and returns
+  /// true, otherwise returns false.  This method is similar to
+  /// writeIfNotFull, but if you don't have a specific need for that
+  /// method you should use this one.
+  ///
+  /// One of the common usages of this method is to enqueue via the
+  /// move constructor, something like q.write(std::move(x)).  If write
+  /// returns false because the queue is full then x has not actually been
+  /// consumed, which looks strange.  To understand why it is actually okay
+  /// to use x afterward, remember that std::move is just a typecast that
+  /// provides an rvalue reference that enables use of a move constructor
+  /// or operator.  std::move doesn't actually move anything.  It could
+  /// more accurately be called std::rvalue_cast or std::move_permission.
+  template <typename ...Args>
+  bool write(Args&&... args) noexcept {
+    uint64_t ticket;
+    if (tryObtainReadyPushTicket(ticket)) {
+      // we have pre-validated that the ticket won't block
+      enqueueWithTicket(ticket, std::forward<Args>(args)...);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  /// If the queue is not full, enqueues and returns true, otherwise
+  /// returns false.  Unlike write this method can be blocked by another
+  /// thread, specifically a read that has linearized (been assigned
+  /// a ticket) but not yet completed.  If you don't really need this
+  /// function you should probably use write.
+  ///
+  /// MPMCQueue isn't lock-free, so just because a read operation has
+  /// linearized (and isFull is false) doesn't mean that space has been
+  /// made available for another write.  In this situation write will
+  /// return false, but writeIfNotFull will wait for the dequeue to finish.
+  /// This method is required if you are composing queues and managing
+  /// your own wakeup, because it guarantees that after every successful
+  /// write a readIfNotEmpty will succeed.
+  template <typename ...Args>
+  bool writeIfNotFull(Args&&... args) noexcept {
+    uint64_t ticket;
+    if (tryObtainPromisedPushTicket(ticket)) {
+      // some other thread is already dequeuing the slot into which we
+      // are going to enqueue, but we might have to wait for them to finish
+      enqueueWithTicket(ticket, std::forward<Args>(args)...);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  /// Moves a dequeued element onto elem, blocking until an element
+  /// is available
+  void blockingRead(T& elem) noexcept {
+    dequeueWithTicket(popTicket_++, elem);
+  }
+
+  /// If an item can be dequeued with no blocking, does so and returns
+  /// true, otherwise returns false.
+  bool read(T& elem) noexcept {
+    uint64_t ticket;
+    if (tryObtainReadyPopTicket(ticket)) {
+      // the ticket has been pre-validated to not block
+      dequeueWithTicket(ticket, elem);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  /// If the queue is not empty, dequeues and returns true, otherwise
+  /// returns false.  If the matching write is still in progress then this
+  /// method may block waiting for it.  If you don't rely on being able
+  /// to dequeue (such as by counting completed write) then you should
+  /// prefer read.
+  bool readIfNotEmpty(T& elem) noexcept {
+    uint64_t ticket;
+    if (tryObtainPromisedPopTicket(ticket)) {
+      // the matching enqueue already has a ticket, but might not be done
+      dequeueWithTicket(ticket, elem);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+ private:
+  enum {
+    /// Once every kAdaptationFreq we will spin longer, to try to estimate
+    /// the proper spin backoff
+    kAdaptationFreq = 128,
+
+    /// To avoid false sharing in slots_ with neighboring memory
+    /// allocations, we pad it with this many SingleElementQueue-s at
+    /// each end
+    kSlotPadding = (detail::CacheLocality::kFalseSharingRange - 1)
+        / sizeof(detail::SingleElementQueue<T,Atom>) + 1
+  };
+
+  /// The maximum number of items in the queue at once
+  size_t FOLLY_ALIGN_TO_AVOID_FALSE_SHARING capacity_;
+
+  /// An array of capacity_ SingleElementQueue-s, each of which holds
+  /// either 0 or 1 item.  We over-allocate by 2 * kSlotPadding and don't
+  /// touch the slots at either end, to avoid false sharing
+  detail::SingleElementQueue<T,Atom>* slots_;
+
+  /// The number of slots_ indices that we advance for each ticket, to
+  /// avoid false sharing.  Ideally slots_[i] and slots_[i + stride_]
+  /// aren't on the same cache line
+  int stride_;
+
+  /// Enqueuers get tickets from here
+  Atom<uint64_t> FOLLY_ALIGN_TO_AVOID_FALSE_SHARING pushTicket_;
+
+  /// Dequeuers get tickets from here
+  Atom<uint64_t> FOLLY_ALIGN_TO_AVOID_FALSE_SHARING popTicket_;
+
+  /// This is how many times we will spin before using FUTEX_WAIT when
+  /// the queue is full on enqueue, adaptively computed by occasionally
+  /// spinning for longer and smoothing with an exponential moving average
+  Atom<uint32_t> FOLLY_ALIGN_TO_AVOID_FALSE_SHARING pushSpinCutoff_;
+
+  /// The adaptive spin cutoff when the queue is empty on dequeue
+  Atom<uint32_t> FOLLY_ALIGN_TO_AVOID_FALSE_SHARING popSpinCutoff_;
+
+  /// Alignment doesn't prevent false sharing at the end of the struct,
+  /// so fill out the last cache line
+  char padding_[detail::CacheLocality::kFalseSharingRange -
+                sizeof(Atom<uint32_t>)];
+
+
+  /// We assign tickets in increasing order, but we don't want to
+  /// access neighboring elements of slots_ because that will lead to
+  /// false sharing (multiple cores accessing the same cache line even
+  /// though they aren't accessing the same bytes in that cache line).
+  /// To avoid this we advance by stride slots per ticket.
+  ///
+  /// We need gcd(capacity, stride) to be 1 so that we will use all
+  /// of the slots.  We ensure this by only considering prime strides,
+  /// which either have no common divisors with capacity or else have
+  /// a zero remainder after dividing by capacity.  That is sufficient
+  /// to guarantee correctness, but we also want to actually spread the
+  /// accesses away from each other to avoid false sharing (consider a
+  /// stride of 7 with a capacity of 8).  To that end we try a few taking
+  /// care to observe that advancing by -1 is as bad as advancing by 1
+  /// when in comes to false sharing.
+  ///
+  /// The simple way to avoid false sharing would be to pad each
+  /// SingleElementQueue, but since we have capacity_ of them that could
+  /// waste a lot of space.
+  static int computeStride(size_t capacity) noexcept {
+    static const int smallPrimes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
+
+    int bestStride = 1;
+    size_t bestSep = 1;
+    for (int stride : smallPrimes) {
+      if ((stride % capacity) == 0 || (capacity % stride) == 0) {
+        continue;
+      }
+      size_t sep = stride % capacity;
+      sep = std::min(sep, capacity - sep);
+      if (sep > bestSep) {
+        bestStride = stride;
+        bestSep = sep;
+      }
+    }
+    return bestStride;
+  }
+
+  /// Returns the index into slots_ that should be used when enqueuing or
+  /// dequeuing with the specified ticket
+  size_t idx(uint64_t ticket) noexcept {
+    return ((ticket * stride_) % capacity_) + kSlotPadding;
+  }
+
+  /// Maps an enqueue or dequeue ticket to the turn should be used at the
+  /// corresponding SingleElementQueue
+  uint32_t turn(uint64_t ticket) noexcept {
+    return ticket / capacity_;
+  }
+
+  /// Tries to obtain a push ticket for which SingleElementQueue::enqueue
+  /// won't block.  Returns true on immediate success, false on immediate
+  /// failure.
+  bool tryObtainReadyPushTicket(uint64_t& rv) noexcept {
+    auto ticket = pushTicket_.load(std::memory_order_acquire); // A
+    while (true) {
+      if (!slots_[idx(ticket)].mayEnqueue(turn(ticket))) {
+        // if we call enqueue(ticket, ...) on the SingleElementQueue
+        // right now it would block, but this might no longer be the next
+        // ticket.  We can increase the chance of tryEnqueue success under
+        // contention (without blocking) by rechecking the ticket dispenser
+        auto prev = ticket;
+        ticket = pushTicket_.load(std::memory_order_acquire); // B
+        if (prev == ticket) {
+          // mayEnqueue was bracketed by two reads (A or prev B or prev
+          // failing CAS to B), so we are definitely unable to enqueue
+          return false;
+        }
+      } else {
+        // we will bracket the mayEnqueue check with a read (A or prev B
+        // or prev failing CAS) and the following CAS.  If the CAS fails
+        // it will effect a load of pushTicket_
+        if (pushTicket_.compare_exchange_strong(ticket, ticket + 1)) {
+          rv = ticket;
+          return true;
+        }
+      }
+    }
+  }
+
+  /// Tries to obtain a push ticket which can be satisfied if all
+  /// in-progress pops complete.  This function does not block, but
+  /// blocking may be required when using the returned ticket if some
+  /// other thread's pop is still in progress (ticket has been granted but
+  /// pop has not yet completed).
+  bool tryObtainPromisedPushTicket(uint64_t& rv) noexcept {
+    auto numPushes = pushTicket_.load(std::memory_order_acquire); // A
+    while (true) {
+      auto numPops = popTicket_.load(std::memory_order_acquire); // B
+      // n will be negative if pops are pending
+      int64_t n = numPushes - numPops;
+      if (n >= static_cast<ssize_t>(capacity_)) {
+        // Full, linearize at B.  We don't need to recheck the read we
+        // performed at A, because if numPushes was stale at B then the
+        // real numPushes value is even worse
+        return false;
+      }
+      if (pushTicket_.compare_exchange_strong(numPushes, numPushes + 1)) {
+        rv = numPushes;
+        return true;
+      }
+    }
+  }
+
+  /// Tries to obtain a pop ticket for which SingleElementQueue::dequeue
+  /// won't block.  Returns true on immediate success, false on immediate
+  /// failure.
+  bool tryObtainReadyPopTicket(uint64_t& rv) noexcept {
+    auto ticket = popTicket_.load(std::memory_order_acquire);
+    while (true) {
+      if (!slots_[idx(ticket)].mayDequeue(turn(ticket))) {
+        auto prev = ticket;
+        ticket = popTicket_.load(std::memory_order_acquire);
+        if (prev == ticket) {
+          return false;
+        }
+      } else {
+        if (popTicket_.compare_exchange_strong(ticket, ticket + 1)) {
+          rv = ticket;
+          return true;
+        }
+      }
+    }
+  }
+
+  /// Similar to tryObtainReadyPopTicket, but returns a pop ticket whose
+  /// corresponding push ticket has already been handed out, rather than
+  /// returning one whose corresponding push ticket has already been
+  /// completed.  This means that there is a possibility that the caller
+  /// will block when using the ticket, but it allows the user to rely on
+  /// the fact that if enqueue has succeeded, tryObtainPromisedPopTicket
+  /// will return true.  The "try" part of this is that we won't have
+  /// to block waiting for someone to call enqueue, although we might
+  /// have to block waiting for them to finish executing code inside the
+  /// MPMCQueue itself.
+  bool tryObtainPromisedPopTicket(uint64_t& rv) noexcept {
+    auto numPops = popTicket_.load(std::memory_order_acquire); // A
+    while (true) {
+      auto numPushes = pushTicket_.load(std::memory_order_acquire); // B
+      if (numPops >= numPushes) {
+        // Empty, or empty with pending pops.  Linearize at B.  We don't
+        // need to recheck the read we performed at A, because if numPops
+        // is stale then the fresh value is larger and the >= is still true
+        return false;
+      }
+      if (popTicket_.compare_exchange_strong(numPops, numPops + 1)) {
+        rv = numPops;
+        return true;
+      }
+    }
+  }
+
+  // Given a ticket, constructs an enqueued item using args
+  template <typename ...Args>
+  void enqueueWithTicket(uint64_t ticket, Args&&... args) noexcept {
+    slots_[idx(ticket)].enqueue(turn(ticket),
+                                pushSpinCutoff_,
+                                (ticket % kAdaptationFreq) == 0,
+                                std::forward<Args>(args)...);
+  }
+
+  // Given a ticket, dequeues the corresponding element
+  void dequeueWithTicket(uint64_t ticket, T& elem) noexcept {
+    slots_[idx(ticket)].dequeue(turn(ticket),
+                                popSpinCutoff_,
+                                (ticket % kAdaptationFreq) == 0,
+                                elem);
+  }
+};
+
+
+namespace detail {
+
+/// SingleElementQueue implements a blocking queue that holds at most one
+/// item, and that requires its users to assign incrementing identifiers
+/// (turns) to each enqueue and dequeue operation.  Note that the turns
+/// used by SingleElementQueue are doubled inside the TurnSequencer
+template <typename T, template <typename> class Atom>
+struct SingleElementQueue {
+
+  ~SingleElementQueue() noexcept {
+    if ((sequencer_.uncompletedTurnLSB() & 1) == 1) {
+      // we are pending a dequeue, so we have a constructed item
+      destroyContents();
+    }
+  }
+
+  /// enqueue using in-place noexcept construction
+  template <typename ...Args,
+            typename = typename std::enable_if<
+              std::is_nothrow_constructible<T,Args...>::value>::type>
+  void enqueue(const uint32_t turn,
+               Atom<uint32_t>& spinCutoff,
+               const bool updateSpinCutoff,
+               Args&&... args) noexcept {
+    sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
+    new (&contents_) T(std::forward<Args>(args)...);
+    sequencer_.completeTurn(turn * 2);
+  }
+
+  /// enqueue using move construction, either real (if
+  /// is_nothrow_move_constructible) or simulated using relocation and
+  /// default construction (if IsRelocatable and has_nothrow_constructor)
+  template <typename = typename std::enable_if<
+                (folly::IsRelocatable<T>::value &&
+                 boost::has_nothrow_constructor<T>::value) ||
+                std::is_nothrow_constructible<T,T&&>::value>::type>
+  void enqueue(const uint32_t turn,
+               Atom<uint32_t>& spinCutoff,
+               const bool updateSpinCutoff,
+               T&& goner) noexcept {
+    enqueueImpl(
+        turn,
+        spinCutoff,
+        updateSpinCutoff,
+        std::move(goner),
+        typename std::conditional<std::is_nothrow_constructible<T,T&&>::value,
+                                  ImplByMove, ImplByRelocation>::type());
+  }
+
+  bool mayEnqueue(const uint32_t turn) const noexcept {
+    return sequencer_.isTurn(turn * 2);
+  }
+
+  void dequeue(uint32_t turn,
+               Atom<uint32_t>& spinCutoff,
+               const bool updateSpinCutoff,
+               T& elem) noexcept {
+    dequeueImpl(turn,
+                spinCutoff,
+                updateSpinCutoff,
+                elem,
+                typename std::conditional<folly::IsRelocatable<T>::value,
+                                          ImplByRelocation,
+                                          ImplByMove>::type());
+  }
+
+  bool mayDequeue(const uint32_t turn) const noexcept {
+    return sequencer_.isTurn(turn * 2 + 1);
+  }
+
+ private:
+  /// Storage for a T constructed with placement new
+  typename std::aligned_storage<sizeof(T),alignof(T)>::type contents_;
+
+  /// Even turns are pushes, odd turns are pops
+  TurnSequencer<Atom> sequencer_;
+
+  T* ptr() noexcept {
+    return static_cast<T*>(static_cast<void*>(&contents_));
+  }
+
+  void destroyContents() noexcept {
+    try {
+      ptr()->~T();
+    } catch (...) {
+      // g++ doesn't seem to have std::is_nothrow_destructible yet
+    }
+#ifndef NDEBUG
+    memset(&contents_, 'Q', sizeof(T));
+#endif
+  }
+
+  /// Tag classes for dispatching to enqueue/dequeue implementation.
+  struct ImplByRelocation {};
+  struct ImplByMove {};
+
+  /// enqueue using nothrow move construction.
+  void enqueueImpl(const uint32_t turn,
+                   Atom<uint32_t>& spinCutoff,
+                   const bool updateSpinCutoff,
+                   T&& goner,
+                   ImplByMove) noexcept {
+    sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
+    new (&contents_) T(std::move(goner));
+    sequencer_.completeTurn(turn * 2);
+  }
+
+  /// enqueue by simulating nothrow move with relocation, followed by
+  /// default construction to a noexcept relocation.
+  void enqueueImpl(const uint32_t turn,
+                   Atom<uint32_t>& spinCutoff,
+                   const bool updateSpinCutoff,
+                   T&& goner,
+                   ImplByRelocation) noexcept {
+    sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);
+    memcpy(&contents_, &goner, sizeof(T));
+    sequencer_.completeTurn(turn * 2);
+    new (&goner) T();
+  }
+
+  /// dequeue by destructing followed by relocation.  This version is preferred,
+  /// because as much work as possible can be done before waiting.
+  void dequeueImpl(uint32_t turn,
+                   Atom<uint32_t>& spinCutoff,
+                   const bool updateSpinCutoff,
+                   T& elem,
+                   ImplByRelocation) noexcept {
+    try {
+      elem.~T();
+    } catch (...) {
+      // unlikely, but if we don't complete our turn the queue will die
+    }
+    sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff);
+    memcpy(&elem, &contents_, sizeof(T));
+    sequencer_.completeTurn(turn * 2 + 1);
+  }
+
+  /// dequeue by nothrow move assignment.
+  void dequeueImpl(uint32_t turn,
+                   Atom<uint32_t>& spinCutoff,
+                   const bool updateSpinCutoff,
+                   T& elem,
+                   ImplByMove) noexcept {
+    sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff);
+    elem = std::move(*ptr());
+    destroyContents();
+    sequencer_.completeTurn(turn * 2 + 1);
+  }
+};
+
+} // namespace detail
+
+} // namespace folly
diff --git a/faux-folly/folly/Makefile.am b/faux-folly/folly/Makefile.am
new file mode 100644
index 0000000..e6e361e
--- /dev/null
+++ b/faux-folly/folly/Makefile.am
@@ -0,0 +1,190 @@
+SUBDIRS = . test
+
+ACLOCAL_AMFLAGS = -I m4
+
+CLEANFILES =
+
+noinst_LTLIBRARIES = \
+	libfollybase.la
+
+lib_LTLIBRARIES = \
+	libfolly.la \
+	libfollybenchmark.la
+
+follyincludedir = $(includedir)/folly
+
+nobase_follyinclude_HEADERS = \
+	ApplyTuple.h \
+	AtomicStruct.h \
+	Baton.h \
+	Benchmark.h \
+	Bits.h \
+	Cancellation.h \
+	Conv.h \
+	CPortability.h \
+	detail/BitIteratorDetail.h \
+	detail/BitsDetail.h \
+	detail/CacheLocality.h \
+	detail/CancellationDetail.h \
+	detail/Clock.h \
+	detail/ExceptionWrapper.h \
+	detail/FileUtilDetail.h \
+	detail/FunctionalExcept.h \
+	detail/Futex.h \
+	detail/Malloc.h \
+	detail/MemoryIdler.h \
+	detail/Sleeper.h \
+	detail/Stats.h \
+	detail/ThreadLocalDetail.h \
+	detail/TurnSequencer.h \
+	detail/UncaughtExceptionCounter.h \
+	Demangle.h \
+	DynamicConverter.h \
+	dynamic.h \
+	dynamic-inl.h \
+	Exception.h \
+	ExceptionWrapper.h \
+	Executor.h \
+	ExecutorWithCancellation.h \
+	FBString.h \
+	FBVector.h \
+	File.h \
+	FileUtil.h \
+	folly-config.h \
+	Foreach.h \
+	FormatArg.h \
+	FormatTraits.h \
+	Format.h \
+	Format-inl.h \
+	futures/Barrier.h \
+	futures/ThreadedExecutor.h \
+	futures/DrivableExecutor.h \
+	futures/Future-pre.h \
+	futures/helpers.h \
+	futures/Future.h \
+	futures/Future-inl.h \
+	futures/FutureException.h \
+	futures/InlineExecutor.h \
+	futures/ManualExecutor.h \
+	futures/OpaqueCallbackShunt.h \
+	futures/Promise-inl.h \
+	futures/Promise.h \
+	futures/QueuedImmediateExecutor.h \
+	futures/ScheduledExecutor.h \
+	futures/SharedPromise.h \
+	futures/SharedPromise-inl.h \
+	futures/Timekeeper.h \
+	futures/Try-inl.h \
+	futures/Try.h \
+	futures/Unit.h \
+	futures/detail/Core.h \
+	futures/detail/FSM.h \
+	futures/detail/ThreadWheelTimekeeper.h \
+	futures/detail/TimerMap.h \
+	futures/detail/Types.h \
+	Hash.h \
+	IndexedMemPool.h \
+	io/Cursor.h \
+	io/IOBuf.h \
+	io/IOBufQueue.h \
+	Lazy.h \
+	LifoSem.h \
+	Likely.h \
+	Logging.h \
+	Malloc.h \
+	MapUtil.h \
+	Memory.h \
+	MicroSpinLock.h \
+	MoveWrapper.h \
+	MPMCQueue.h \
+	Optional.h \
+	Portability.h \
+	Preprocessor.h \
+	Random.h \
+	Random-inl.h \
+	Range.h \
+	ScopeGuard.h \
+	SpookyHashV1.h \
+	SpookyHashV2.h \
+	stats/BucketedTimeSeries-defs.h \
+	stats/BucketedTimeSeries.h \
+	stats/Histogram-defs.h \
+	stats/Histogram.h \
+	stats/MultiLevelTimeSeries-defs.h \
+	stats/MultiLevelTimeSeries.h \
+	String.h \
+	String-inl.h \
+	test/FBStringTestBenchmarks.cpp.h \
+	test/FBVectorTestBenchmarks.cpp.h \
+	test/function_benchmark/benchmark_impl.h \
+	test/function_benchmark/test_functions.h \
+	ThreadCachedInt.h \
+	ThreadLocal.h \
+	Traits.h \
+	Varint.h \
+	VersionCheck.h
+
+FormatTables.cpp: build/generate_format_tables.py
+	$(PYTHON) build/generate_format_tables.py
+CLEANFILES += FormatTables.cpp
+
+EscapeTables.cpp: build/generate_escape_tables.py
+	$(PYTHON) build/generate_escape_tables.py
+CLEANFILES += EscapeTables.cpp
+
+libfollybase_la_SOURCES = \
+	Conv.cpp \
+	Demangle.cpp \
+	EscapeTables.cpp \
+	Format.cpp \
+	FormatTables.cpp \
+	Malloc.cpp \
+	Range.cpp \
+	StringBase.cpp \
+	String.cpp
+
+libfolly_la_SOURCES = \
+	detail/CacheLocality.cpp \
+	dynamic.cpp \
+	File.cpp \
+	FileUtil.cpp \
+	futures/detail/ThreadWheelTimekeeper.cpp \
+	futures/detail/TimerMap.cpp \
+	futures/Barrier.cpp \
+	futures/ThreadedExecutor.cpp \
+	futures/Future.cpp \
+	futures/InlineExecutor.cpp \
+	futures/ManualExecutor.cpp \
+	futures/QueuedImmediateExecutor.cpp \
+	futures/ScheduledExecutor.cpp \
+	detail/Futex.cpp \
+	LifoSem.cpp \
+	io/IOBuf.cpp \
+	io/IOBufQueue.cpp \
+	detail/MemoryIdler.cpp \
+	Random.cpp \
+	SpookyHashV1.cpp \
+	SpookyHashV2.cpp \
+	stats/Instantiations.cpp \
+	Version.cpp
+
+if !HAVE_LINUX
+libfollybase_la_SOURCES += detail/Clock.cpp
+endif
+
+if !HAVE_WEAK_SYMBOLS
+libfollybase_la_SOURCES += detail/MallocImpl.cpp
+endif
+
+if !HAVE_BITS_FUNCTEXCEPT
+libfollybase_la_SOURCES += detail/FunctionalExcept.cpp
+endif
+
+libfollybase_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LT_VERSION)
+
+libfolly_la_LIBADD = libfollybase.la
+libfolly_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LT_VERSION)
+
+libfollybenchmark_la_SOURCES = Benchmark.cpp
+libfollybenchmark_la_LIBADD = libfolly.la
+libfollybenchmark_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LT_VERSION)
diff --git a/faux-folly/folly/Malloc.cpp b/faux-folly/folly/Malloc.cpp
new file mode 100644
index 0000000..aa2175c
--- /dev/null
+++ b/faux-folly/folly/Malloc.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 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/Malloc.h>
+
+#include <cstdint>
+
+namespace folly {
+
+// How do we determine that we're using jemalloc?
+// In the hackiest way possible. We allocate memory using malloc() and see if
+// the per-thread counter of allocated memory increases. This makes me feel
+// dirty inside. Also note that this requires jemalloc to have been compiled
+// with --enable-stats.
+bool usingJEMallocSlow() {
+  // Some platforms (*cough* OSX *cough*) require weak symbol checks to be
+  // in the form if (mallctl != nullptr). Not if (mallctl) or if (!mallctl)
+  // (!!). http://goo.gl/xpmctm
+  if (mallocx == nullptr || rallocx == nullptr || xallocx == nullptr
+      || sallocx == nullptr || dallocx == nullptr || nallocx == nullptr
+      || mallctl == nullptr || mallctlnametomib == nullptr
+      || mallctlbymib == nullptr) {
+    return false;
+  }
+
+  // "volatile" because gcc optimizes out the reads from *counter, because
+  // it "knows" malloc doesn't modify global state...
+  /* nolint */ volatile uint64_t* counter;
+  size_t counterLen = sizeof(uint64_t*);
+
+  if (mallctl("thread.allocatedp", static_cast<void*>(&counter), &counterLen,
+              nullptr, 0) != 0) {
+    return false;
+  }
+
+  if (counterLen != sizeof(uint64_t*)) {
+    return false;
+  }
+
+  uint64_t origAllocated = *counter;
+
+  void* ptr = malloc(1);
+  if (!ptr) {
+    // wtf, failing to allocate 1 byte
+    return false;
+  }
+  free(ptr);
+
+  return (origAllocated != *counter);
+}
+
+}  // namespaces
diff --git a/faux-folly/folly/Malloc.h b/faux-folly/folly/Malloc.h
new file mode 100644
index 0000000..dab215d
--- /dev/null
+++ b/faux-folly/folly/Malloc.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// Functions to provide smarter use of jemalloc, if jemalloc is being used.
+// http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html
+
+#ifndef FOLLY_MALLOC_H_
+#define FOLLY_MALLOC_H_
+
+/**
+ * Define various MALLOCX_* macros normally provided by jemalloc.  We define
+ * them so that we don't have to include jemalloc.h, in case the program is
+ * built without jemalloc support.
+ */
+#ifndef MALLOCX_LG_ALIGN
+#define MALLOCX_LG_ALIGN(la) (la)
+#endif
+#ifndef MALLOCX_ZERO
+#define MALLOCX_ZERO (static_cast<int>(0x40))
+#endif
+
+// If using fbstring from libstdc++, then just define stub code
+// here to typedef the fbstring type into the folly namespace.
+// This provides backwards compatibility for code that explicitly
+// includes and uses fbstring.
+#if defined(_GLIBCXX_USE_FB) && !defined(_LIBSTDCXX_FBSTRING)
+
+#include <folly/detail/Malloc.h>
+
+#include <string>
+
+namespace folly {
+  using std::goodMallocSize;
+  using std::jemallocMinInPlaceExpandable;
+  using std::usingJEMalloc;
+  using std::smartRealloc;
+  using std::checkedMalloc;
+  using std::checkedCalloc;
+  using std::checkedRealloc;
+}
+
+#else // !defined(_GLIBCXX_USE_FB) || defined(_LIBSTDCXX_FBSTRING)
+
+#ifdef _LIBSTDCXX_FBSTRING
+#pragma GCC system_header
+
+/**
+ * Declare *allocx() and mallctl*() as weak symbols. These will be provided by
+ * jemalloc if we are using jemalloc, or will be NULL if we are using another
+ * malloc implementation.
+ */
+extern "C" void* mallocx(size_t, int)
+__attribute__((__weak__));
+extern "C" void* rallocx(void*, size_t, int)
+__attribute__((__weak__));
+extern "C" size_t xallocx(void*, size_t, size_t, int)
+__attribute__((__weak__));
+extern "C" size_t sallocx(const void*, int)
+__attribute__((__weak__));
+extern "C" void dallocx(void*, int)
+__attribute__((__weak__));
+extern "C" size_t nallocx(size_t, int)
+__attribute__((__weak__));
+extern "C" int mallctl(const char*, void*, size_t*, void*, size_t)
+__attribute__((__weak__));
+extern "C" int mallctlnametomib(const char*, size_t*, size_t*)
+__attribute__((__weak__));
+extern "C" int mallctlbymib(const size_t*, size_t, void*, size_t*, void*,
+                            size_t)
+__attribute__((__weak__));
+
+#include <bits/functexcept.h>
+#define FOLLY_HAVE_MALLOC_H 1
+#else
+#include <folly/detail/Malloc.h> /* nolint */
+#include <folly/Portability.h>
+#endif
+
+// for malloc_usable_size
+// NOTE: FreeBSD 9 doesn't have malloc.h.  It's defitions
+// are found in stdlib.h.
+#if FOLLY_HAVE_MALLOC_H
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+
+#include <new>
+
+#ifdef _LIBSTDCXX_FBSTRING
+namespace std _GLIBCXX_VISIBILITY(default) {
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+#else
+namespace folly {
+#endif
+
+bool usingJEMallocSlow();
+
+/**
+ * Determine if we are using jemalloc or not.
+ */
+inline bool usingJEMalloc() {
+  // Checking for rallocx != NULL is not sufficient; we may be in a dlopen()ed
+  // module that depends on libjemalloc, so rallocx is resolved, but the main
+  // program might be using a different memory allocator. Look at the
+  // implementation of usingJEMallocSlow() for the (hacky) details.
+  static const bool result = usingJEMallocSlow();
+  return result;
+}
+
+/**
+ * For jemalloc's size classes, see
+ * http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html
+ */
+inline size_t goodMallocSize(size_t minSize) noexcept {
+  if (!usingJEMalloc()) {
+    // Not using jemalloc - no smarts
+    return minSize;
+  }
+  size_t goodSize;
+  if (minSize <= 64) {
+    // Choose smallest allocation to be 64 bytes - no tripping over
+    // cache line boundaries, and small string optimization takes care
+    // of short strings anyway.
+    goodSize = 64;
+  } else if (minSize <= 512) {
+    // Round up to the next multiple of 64; we don't want to trip over
+    // cache line boundaries.
+    goodSize = (minSize + 63) & ~size_t(63);
+  } else {
+    // Boundaries between size classes depend on numerious factors, some of
+    // which can even be modified at run-time. Determine the good allocation
+    // size by calling nallocx() directly.
+    goodSize = nallocx(minSize, 0);
+  }
+  assert(nallocx(goodSize, 0) == goodSize);
+  return goodSize;
+}
+
+// We always request "good" sizes for allocation, so jemalloc can
+// never grow in place small blocks; they're already occupied to the
+// brim.  Blocks larger than or equal to 4096 bytes can in fact be
+// expanded in place, and this constant reflects that.
+static const size_t jemallocMinInPlaceExpandable = 4096;
+
+/**
+ * Trivial wrappers around malloc, calloc, realloc that check for allocation
+ * failure and throw std::bad_alloc in that case.
+ */
+inline void* checkedMalloc(size_t size) {
+  void* p = malloc(size);
+  if (!p) std::__throw_bad_alloc();
+  return p;
+}
+
+inline void* checkedCalloc(size_t n, size_t size) {
+  void* p = calloc(n, size);
+  if (!p) std::__throw_bad_alloc();
+  return p;
+}
+
+inline void* checkedRealloc(void* ptr, size_t size) {
+  void* p = realloc(ptr, size);
+  if (!p) std::__throw_bad_alloc();
+  return p;
+}
+
+/**
+ * This function tries to reallocate a buffer of which only the first
+ * currentSize bytes are used. The problem with using realloc is that
+ * if currentSize is relatively small _and_ if realloc decides it
+ * needs to move the memory chunk to a new buffer, then realloc ends
+ * up copying data that is not used. It's impossible to hook into
+ * GNU's malloc to figure whether expansion will occur in-place or as
+ * a malloc-copy-free troika. (If an expand_in_place primitive would
+ * be available, smartRealloc would use it.) As things stand, this
+ * routine just tries to call realloc() (thus benefitting of potential
+ * copy-free coalescing) unless there's too much slack memory.
+ */
+inline void* smartRealloc(void* p,
+                          const size_t currentSize,
+                          const size_t currentCapacity,
+                          const size_t newCapacity) {
+  assert(p);
+  assert(currentSize <= currentCapacity &&
+         currentCapacity < newCapacity);
+
+  if (usingJEMalloc()) {
+    // using jemalloc's API. Don't forget that jemalloc can never grow
+    // in place blocks smaller than 4096 bytes.
+    //
+    // NB: newCapacity may not be precisely equal to a jemalloc size class,
+    // i.e. newCapacity is not guaranteed to be the result of a
+    // goodMallocSize() call, therefore xallocx() may return more than
+    // newCapacity bytes of space.  Use >= rather than == to check whether
+    // xallocx() successfully expanded in place.
+    if (currentCapacity >= jemallocMinInPlaceExpandable &&
+        xallocx(p, newCapacity, 0, 0) >= newCapacity) {
+      // Managed to expand in place
+      return p;
+    }
+    // Cannot expand; must move
+    auto const result = checkedMalloc(newCapacity);
+    std::memcpy(result, p, currentSize);
+    free(p);
+    return result;
+  }
+
+  // No jemalloc no honey
+  auto const slack = currentCapacity - currentSize;
+  if (slack * 2 > currentSize) {
+    // Too much slack, malloc-copy-free cycle:
+    auto const result = checkedMalloc(newCapacity);
+    std::memcpy(result, p, currentSize);
+    free(p);
+    return result;
+  }
+  // If there's not too much slack, we realloc in hope of coalescing
+  return checkedRealloc(p, newCapacity);
+}
+
+#ifdef _LIBSTDCXX_FBSTRING
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif
+
+} // folly
+
+#endif // !defined(_GLIBCXX_USE_FB) || defined(_LIBSTDCXX_FBSTRING)
+
+#endif // FOLLY_MALLOC_H_
diff --git a/faux-folly/folly/MapUtil.h b/faux-folly/folly/MapUtil.h
new file mode 100644
index 0000000..b00bccf
--- /dev/null
+++ b/faux-folly/folly/MapUtil.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_MAPUTIL_H_
+#define FOLLY_MAPUTIL_H_
+
+#include <folly/Conv.h>
+#include <folly/Optional.h>
+
+namespace folly {
+
+/**
+ * Given a map and a key, return the value corresponding to the key in the map,
+ * or a given default value if the key doesn't exist in the map.
+ */
+template <class Map>
+typename Map::mapped_type get_default(
+    const Map& map, const typename Map::key_type& key,
+    const typename Map::mapped_type& dflt =
+    typename Map::mapped_type()) {
+  auto pos = map.find(key);
+  return (pos != map.end() ? pos->second : dflt);
+}
+
+/**
+ * Given a map and a key, return the value corresponding to the key in the map,
+ * or throw an exception of the specified type.
+ */
+template <class E = std::out_of_range, class Map>
+typename Map::mapped_type get_or_throw(
+    const Map& map, const typename Map::key_type& key,
+    const std::string& exceptionStrPrefix = std::string()) {
+  auto pos = map.find(key);
+  if (pos != map.end()) {
+    return pos->second;
+  }
+  throw E(folly::to<std::string>(exceptionStrPrefix, key));
+}
+
+/**
+ * Given a map and a key, return a Optional<V> if the key exists and None if the
+ * key does not exist in the map.
+ */
+template <class Map>
+folly::Optional<typename Map::mapped_type> get_optional(
+    const Map& map, const typename Map::key_type& key) {
+  auto pos = map.find(key);
+  if (pos != map.end()) {
+    return folly::Optional<typename Map::mapped_type>(pos->second);
+  } else {
+    return folly::none;
+  }
+}
+
+/**
+ * Given a map and a key, return a reference to the value corresponding to the
+ * key in the map, or the given default reference if the key doesn't exist in
+ * the map.
+ */
+template <class Map>
+const typename Map::mapped_type& get_ref_default(
+    const Map& map, const typename Map::key_type& key,
+    const typename Map::mapped_type& dflt) {
+  auto pos = map.find(key);
+  return (pos != map.end() ? pos->second : dflt);
+}
+
+/**
+ * Given a map and a key, return a pointer to the value corresponding to the
+ * key in the map, or nullptr if the key doesn't exist in the map.
+ */
+template <class Map>
+const typename Map::mapped_type* get_ptr(
+    const Map& map, const typename Map::key_type& key) {
+  auto pos = map.find(key);
+  return (pos != map.end() ? &pos->second : nullptr);
+}
+
+/**
+ * Non-const overload of the above.
+ */
+template <class Map>
+typename Map::mapped_type* get_ptr(
+    Map& map, const typename Map::key_type& key) {
+  auto pos = map.find(key);
+  return (pos != map.end() ? &pos->second : nullptr);
+}
+
+}  // namespace folly
+
+#endif /* FOLLY_MAPUTIL_H_ */
diff --git a/faux-folly/folly/Memory.h b/faux-folly/folly/Memory.h
new file mode 100644
index 0000000..cf32e74
--- /dev/null
+++ b/faux-folly/folly/Memory.h
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_MEMORY_H_
+#define FOLLY_MEMORY_H_
+
+#include <folly/Traits.h>
+
+#include <cstddef>
+#include <cstdlib>
+#include <exception>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+
+namespace folly {
+
+/**
+ * For exception safety and consistency with make_shared. Erase me when
+ * we have std::make_unique().
+ *
+ * @author Louis Brandy (ldbrandy@fb.com)
+ * @author Xu Ning (xning@fb.com)
+ */
+
+template<typename T, typename Dp = std::default_delete<T>, typename... Args>
+typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T, Dp>>::type
+make_unique(Args&&... args) {
+  return std::unique_ptr<T, Dp>(new T(std::forward<Args>(args)...));
+}
+
+// Allows 'make_unique<T[]>(10)'. (N3690 s20.9.1.4 p3-4)
+template<typename T, typename Dp = std::default_delete<T>>
+typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T, Dp>>::type
+make_unique(const size_t n) {
+  return std::unique_ptr<T, Dp>(new typename std::remove_extent<T>::type[n]());
+}
+
+// Disallows 'make_unique<T[10]>()'. (N3690 s20.9.1.4 p5)
+template<typename T, typename Dp = std::default_delete<T>, typename... Args>
+typename std::enable_if<
+  std::extent<T>::value != 0, std::unique_ptr<T, Dp>>::type
+make_unique(Args&&...) = delete;
+
+/**
+ * static_function_deleter
+ *
+ * So you can write this:
+ *
+ *      using RSA_deleter = folly::static_function_deleter<RSA, &RSA_free>;
+ *      auto rsa = std::unique_ptr<RSA, RSA_deleter>(RSA_new());
+ *      RSA_generate_key_ex(rsa.get(), bits, exponent, nullptr);
+ *      rsa = nullptr;  // calls RSA_free(rsa.get())
+ *
+ * This would be sweet as well for BIO, but unfortunately BIO_free has signature
+ * int(BIO*) while we require signature void(BIO*). So you would need to make a
+ * wrapper for it:
+ *
+ *      inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); }
+ *      using BIO_deleter = folly::static_function_deleter<BIO, &BIO_free_fb>;
+ *      auto buf = std::unique_ptr<BIO, BIO_deleter>(BIO_new(BIO_s_mem()));
+ *      buf = nullptr;  // calls BIO_free(buf.get())
+ */
+
+template <typename T, void(*f)(T*)>
+struct static_function_deleter {
+  void operator()(T* t) { f(t); }
+};
+
+/**
+ *  to_shared_ptr
+ *
+ *  Convert unique_ptr to shared_ptr without specifying the template type
+ *  parameter and letting the compiler deduce it.
+ *
+ *  So you can write this:
+ *
+ *      auto sptr = to_shared_ptr(getSomethingUnique<T>());
+ *
+ *  Instead of this:
+ *
+ *      auto sptr = shared_ptr<T>(getSomethingUnique<T>());
+ *
+ *  Useful when `T` is long, such as:
+ *
+ *      using T = foobar::cpp2::FooBarServiceAsyncClient;
+ */
+template <typename T>
+std::shared_ptr<T> to_shared_ptr(std::unique_ptr<T>&& ptr) {
+  return std::shared_ptr<T>(std::move(ptr));
+}
+
+/**
+ * A SimpleAllocator must provide two methods:
+ *
+ *    void* allocate(size_t size);
+ *    void deallocate(void* ptr);
+ *
+ * which, respectively, allocate a block of size bytes (aligned to the
+ * maximum alignment required on your system), throwing std::bad_alloc
+ * if the allocation can't be satisfied, and free a previously
+ * allocated block.
+ *
+ * SysAlloc resembles the standard allocator.
+ */
+class SysAlloc {
+ public:
+  void* allocate(size_t size) {
+    void* p = ::malloc(size);
+    if (!p) throw std::bad_alloc();
+    return p;
+  }
+  void deallocate(void* p) {
+    ::free(p);
+  }
+};
+
+/**
+ * StlAllocator wraps a SimpleAllocator into a STL-compliant
+ * allocator, maintaining an instance pointer to the simple allocator
+ * object.  The underlying SimpleAllocator object must outlive all
+ * instances of StlAllocator using it.
+ *
+ * But note that if you pass StlAllocator<MallocAllocator,...> to a
+ * standard container it will be larger due to the contained state
+ * pointer.
+ *
+ * @author: Tudor Bosman <tudorb@fb.com>
+ */
+
+// This would be so much simpler with std::allocator_traits, but gcc 4.6.2
+// doesn't support it.
+template <class Alloc, class T> class StlAllocator;
+
+template <class Alloc> class StlAllocator<Alloc, void> {
+ public:
+  typedef void value_type;
+  typedef void* pointer;
+  typedef const void* const_pointer;
+
+  StlAllocator() : alloc_(nullptr) { }
+  explicit StlAllocator(Alloc* a) : alloc_(a) { }
+
+  Alloc* alloc() const {
+    return alloc_;
+  }
+
+  template <class U> struct rebind {
+    typedef StlAllocator<Alloc, U> other;
+  };
+
+  bool operator!=(const StlAllocator<Alloc, void>& other) const {
+    return alloc_ != other.alloc_;
+  }
+
+  bool operator==(const StlAllocator<Alloc, void>& other) const {
+    return alloc_ == other.alloc_;
+  }
+
+ private:
+  Alloc* alloc_;
+};
+
+template <class Alloc, class T>
+class StlAllocator {
+ public:
+  typedef T value_type;
+  typedef T* pointer;
+  typedef const T* const_pointer;
+  typedef T& reference;
+  typedef const T& const_reference;
+
+  typedef ptrdiff_t difference_type;
+  typedef size_t size_type;
+
+  StlAllocator() : alloc_(nullptr) { }
+  explicit StlAllocator(Alloc* a) : alloc_(a) { }
+
+  template <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
+    : alloc_(other.alloc()) { }
+
+  T* allocate(size_t n, const void* hint = nullptr) {
+    return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
+  }
+
+  void deallocate(T* p, size_t n) {
+    alloc_->deallocate(p);
+  }
+
+  size_t max_size() const {
+    return std::numeric_limits<size_t>::max();
+  }
+
+  T* address(T& x) const {
+    return std::addressof(x);
+  }
+
+  const T* address(const T& x) const {
+    return std::addressof(x);
+  }
+
+  template <class... Args>
+  void construct(T* p, Args&&... args) {
+    new (p) T(std::forward<Args>(args)...);
+  }
+
+  void destroy(T* p) {
+    p->~T();
+  }
+
+  Alloc* alloc() const {
+    return alloc_;
+  }
+
+  template <class U> struct rebind {
+    typedef StlAllocator<Alloc, U> other;
+  };
+
+  bool operator!=(const StlAllocator<Alloc, T>& other) const {
+    return alloc_ != other.alloc_;
+  }
+
+  bool operator==(const StlAllocator<Alloc, T>& other) const {
+    return alloc_ == other.alloc_;
+  }
+
+ private:
+  Alloc* alloc_;
+};
+
+/**
+ * Helper function to obtain rebound allocators
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+template <typename T, typename Allocator>
+typename Allocator::template rebind<T>::other rebind_allocator(
+  Allocator const& allocator
+) {
+  return typename Allocator::template rebind<T>::other(allocator);
+}
+
+/*
+ * Helper classes/functions for creating a unique_ptr using a custom
+ * allocator.
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+
+// Derives from the allocator to take advantage of the empty base
+// optimization when possible.
+template <typename Allocator>
+class allocator_delete
+  : private std::remove_reference<Allocator>::type
+{
+  typedef typename std::remove_reference<Allocator>::type allocator_type;
+
+public:
+  typedef typename Allocator::pointer pointer;
+
+  allocator_delete() = default;
+
+  explicit allocator_delete(const allocator_type& allocator)
+    : allocator_type(allocator)
+  {}
+
+  explicit allocator_delete(allocator_type&& allocator)
+    : allocator_type(std::move(allocator))
+  {}
+
+  template <typename U>
+  allocator_delete(const allocator_delete<U>& other)
+    : allocator_type(other.get_allocator())
+  {}
+
+  allocator_type& get_allocator() const {
+    return *const_cast<allocator_delete*>(this);
+  }
+
+  void operator()(pointer p) const {
+    if (!p) return;
+    const_cast<allocator_delete*>(this)->destroy(p);
+    const_cast<allocator_delete*>(this)->deallocate(p, 1);
+  }
+};
+
+template <typename T, typename Allocator>
+class is_simple_allocator {
+  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
+
+  typedef typename std::remove_const<
+    typename std::remove_reference<Allocator>::type
+  >::type allocator;
+  typedef typename std::remove_reference<T>::type value_type;
+  typedef value_type* pointer;
+
+public:
+  constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
+    && !has_destroy<allocator, void(void*)>::value;
+};
+
+template <typename T, typename Allocator>
+struct as_stl_allocator {
+  typedef typename std::conditional<
+    is_simple_allocator<T, Allocator>::value,
+    folly::StlAllocator<
+      typename std::remove_reference<Allocator>::type,
+      typename std::remove_reference<T>::type
+    >,
+    typename std::remove_reference<Allocator>::type
+  >::type type;
+};
+
+template <typename T, typename Allocator>
+typename std::enable_if<
+  is_simple_allocator<T, Allocator>::value,
+  folly::StlAllocator<
+    typename std::remove_reference<Allocator>::type,
+    typename std::remove_reference<T>::type
+  >
+>::type make_stl_allocator(Allocator&& allocator) {
+  return folly::StlAllocator<
+    typename std::remove_reference<Allocator>::type,
+    typename std::remove_reference<T>::type
+  >(&allocator);
+}
+
+template <typename T, typename Allocator>
+typename std::enable_if<
+  !is_simple_allocator<T, Allocator>::value,
+  typename std::remove_reference<Allocator>::type
+>::type make_stl_allocator(Allocator&& allocator) {
+  return std::move(allocator);
+}
+
+/**
+ * AllocatorUniquePtr: a unique_ptr that supports both STL-style
+ * allocators and SimpleAllocator
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+
+template <typename T, typename Allocator>
+struct AllocatorUniquePtr {
+  typedef std::unique_ptr<T,
+    folly::allocator_delete<
+      typename std::conditional<
+        is_simple_allocator<T, Allocator>::value,
+        folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
+        typename std::remove_reference<Allocator>::type
+      >::type
+    >
+  > type;
+};
+
+/**
+ * Functions to allocate a unique_ptr / shared_ptr, supporting both
+ * STL-style allocators and SimpleAllocator, analog to std::allocate_shared
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+
+template <typename T, typename Allocator, typename ...Args>
+typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
+  Allocator&& allocator, Args&&... args
+) {
+  auto stlAllocator = folly::make_stl_allocator<T>(
+    std::forward<Allocator>(allocator)
+  );
+  auto p = stlAllocator.allocate(1);
+
+  try {
+    stlAllocator.construct(p, std::forward<Args>(args)...);
+
+    return {p,
+      folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
+    };
+  } catch (...) {
+    stlAllocator.deallocate(p, 1);
+    throw;
+  }
+}
+
+template <typename T, typename Allocator, typename ...Args>
+std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
+  return std::allocate_shared<T>(
+    folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
+    std::forward<Args>(args)...
+  );
+}
+
+/**
+ * IsArenaAllocator<T>::value describes whether SimpleAllocator has
+ * no-op deallocate().
+ */
+template <class T> struct IsArenaAllocator : std::false_type { };
+
+}  // namespace folly
+
+#endif /* FOLLY_MEMORY_H_ */
diff --git a/faux-folly/folly/Merge.h b/faux-folly/folly/Merge.h
new file mode 100644
index 0000000..7e92c02
--- /dev/null
+++ b/faux-folly/folly/Merge.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_MERGE_H_
+#define FOLLY_MERGE_H_
+
+#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);
+}
+
+}
+
+#endif
diff --git a/faux-folly/folly/MicroSpinLock.h b/faux-folly/folly/MicroSpinLock.h
new file mode 100644
index 0000000..256ab86
--- /dev/null
+++ b/faux-folly/folly/MicroSpinLock.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2015 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
+
+/*
+ * @author Keith Adams <kma@fb.com>
+ * @author Jordan DeLong <delong.j@fb.com>
+ */
+
+#include <array>
+#include <cinttypes>
+#include <type_traits>
+#include <boost/noncopyable.hpp>
+#include <cstdlib>
+#include <pthread.h>
+#include <mutex>
+#include <atomic>
+
+#include <glog/logging.h>
+#include <folly/detail/Sleeper.h>
+#include <folly/Portability.h>
+
+namespace folly {
+
+/*
+ * A really, *really* small spinlock for fine-grained locking of lots
+ * of teeny-tiny data.
+ *
+ * Zero initializing these is guaranteed to be as good as calling
+ * init(), since the free state is guaranteed to be all-bits zero.
+ *
+ * This class should be kept a POD, so we can used it in other packed
+ * structs (gcc does not allow __attribute__((__packed__)) on structs that
+ * contain non-POD data).  This means avoid adding a constructor, or
+ * making some members private, etc.
+ */
+struct MicroSpinLock {
+  enum { FREE = 0, LOCKED = 1 };
+  // lock_ can't be std::atomic<> to preserve POD-ness.
+  uint8_t lock_;
+
+  // Initialize this MSL.  It is unnecessary to call this if you
+  // zero-initialize the MicroSpinLock.
+  void init() {
+    payload()->store(FREE);
+  }
+
+  bool try_lock() {
+    return cas(FREE, LOCKED);
+  }
+
+  void lock() {
+    detail::Sleeper sleeper;
+    do {
+      while (payload()->load() != FREE) {
+        sleeper.wait();
+      }
+    } while (!try_lock());
+    DCHECK(payload()->load() == LOCKED);
+  }
+
+  void unlock() {
+    CHECK(payload()->load() == LOCKED);
+    payload()->store(FREE, std::memory_order_release);
+  }
+
+ private:
+  std::atomic<uint8_t>* payload() {
+    return reinterpret_cast<std::atomic<uint8_t>*>(&this->lock_);
+  }
+
+  bool cas(uint8_t compare, uint8_t newVal) {
+    return std::atomic_compare_exchange_strong_explicit(payload(), &compare, newVal,
+                                                        std::memory_order_acquire,
+                                                        std::memory_order_relaxed);
+  }
+};
+
+//////////////////////////////////////////////////////////////////////
+
+/**
+ * Array of spinlocks where each one is padded to prevent false sharing.
+ * Useful for shard-based locking implementations in environments where
+ * contention is unlikely.
+ */
+
+// TODO: generate it from configure (`getconf LEVEL1_DCACHE_LINESIZE`)
+#define FOLLY_CACHE_LINE_SIZE 64
+
+template <class T, size_t N>
+struct FOLLY_ALIGNED_MAX SpinLockArray {
+  T& operator[](size_t i) {
+    return data_[i].lock;
+  }
+
+  const T& operator[](size_t i) const {
+    return data_[i].lock;
+  }
+
+  constexpr size_t size() const { return N; }
+
+ private:
+  struct PaddedSpinLock {
+    PaddedSpinLock() : lock() {}
+    T lock;
+    char padding[FOLLY_CACHE_LINE_SIZE - sizeof(T)];
+  };
+  static_assert(sizeof(PaddedSpinLock) == FOLLY_CACHE_LINE_SIZE,
+                "Invalid size of PaddedSpinLock");
+
+  // Check if T can theoretically cross a cache line.
+  static_assert(alignof(std::max_align_t) > 0 &&
+                FOLLY_CACHE_LINE_SIZE % alignof(std::max_align_t) == 0 &&
+                sizeof(T) <= alignof(std::max_align_t),
+                "T can cross cache line boundaries");
+
+  char padding_[FOLLY_CACHE_LINE_SIZE];
+  std::array<PaddedSpinLock, N> data_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+typedef std::lock_guard<MicroSpinLock> MSLGuard;
+
+//////////////////////////////////////////////////////////////////////
+
+}
diff --git a/faux-folly/folly/MoveWrapper.h b/faux-folly/folly/MoveWrapper.h
new file mode 100644
index 0000000..853f50e
--- /dev/null
+++ b/faux-folly/folly/MoveWrapper.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 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 <memory>
+
+namespace folly {
+
+/** C++11 closures don't support move-in capture. Nor does std::bind.
+    facepalm.
+
+    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html
+
+    "[...] a work-around that should make people's stomach crawl:
+    write a wrapper that performs move-on-copy, much like the deprecated
+    auto_ptr"
+
+    Unlike auto_ptr, this doesn't require a heap allocation.
+    */
+template <class T>
+class MoveWrapper {
+ public:
+  /** If value can be default-constructed, why not?
+      Then we don't have to move it in */
+  MoveWrapper() = default;
+
+  /// Move a value in.
+  explicit
+  MoveWrapper(T&& t) : value(std::move(t)) {}
+
+  /// copy is move
+  MoveWrapper(const MoveWrapper& other) : value(std::move(other.value)) {}
+
+  /// move is also move
+  MoveWrapper(MoveWrapper&& other) : value(std::move(other.value)) {}
+
+  const T& operator*() const { return value; }
+        T& operator*()       { return value; }
+
+  const T* operator->() const { return &value; }
+        T* operator->()       { return &value; }
+
+  /// move the value out (sugar for std::move(*moveWrapper))
+  T&& move() { return std::move(value); }
+
+  // If you want these you're probably doing it wrong, though they'd be
+  // easy enough to implement
+  MoveWrapper& operator=(MoveWrapper const&) = delete;
+  MoveWrapper& operator=(MoveWrapper&&) = delete;
+
+ private:
+  mutable T value;
+};
+
+/// Make a MoveWrapper from the argument. Because the name "makeMoveWrapper"
+/// is already quite transparent in its intent, this will work for lvalues as
+/// if you had wrapped them in std::move.
+template <class T, class T0 = typename std::remove_reference<T>::type>
+MoveWrapper<T0> makeMoveWrapper(T&& t) {
+  return MoveWrapper<T0>(std::forward<T0>(t));
+}
+
+} // namespace
diff --git a/faux-folly/folly/Optional.h b/faux-folly/folly/Optional.h
new file mode 100644
index 0000000..def3670
--- /dev/null
+++ b/faux-folly/folly/Optional.h
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_OPTIONAL_H_
+#define FOLLY_OPTIONAL_H_
+
+/*
+ * Optional - For conditional initialization of values, like boost::optional,
+ * but with support for move semantics and emplacement.  Reference type support
+ * has not been included due to limited use cases and potential confusion with
+ * semantics of assignment: Assigning to an optional reference could quite
+ * reasonably copy its value or redirect the reference.
+ *
+ * Optional can be useful when a variable might or might not be needed:
+ *
+ *  Optional<Logger> maybeLogger = ...;
+ *  if (maybeLogger) {
+ *    maybeLogger->log("hello");
+ *  }
+ *
+ * Optional enables a 'null' value for types which do not otherwise have
+ * nullability, especially useful for parameter passing:
+ *
+ * void testIterator(const unique_ptr<Iterator>& it,
+ *                   initializer_list<int> idsExpected,
+ *                   Optional<initializer_list<int>> ranksExpected = none) {
+ *   for (int i = 0; it->next(); ++i) {
+ *     EXPECT_EQ(it->doc().id(), idsExpected[i]);
+ *     if (ranksExpected) {
+ *       EXPECT_EQ(it->doc().rank(), (*ranksExpected)[i]);
+ *     }
+ *   }
+ * }
+ *
+ * Optional models OptionalPointee, so calling 'get_pointer(opt)' will return a
+ * pointer to nullptr if the 'opt' is empty, and a pointer to the value if it is
+ * not:
+ *
+ *  Optional<int> maybeInt = ...;
+ *  if (int* v = get_pointer(maybeInt)) {
+ *    cout << *v << endl;
+ *  }
+ */
+#include <cstddef>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+#include <boost/operators.hpp>
+
+#include <folly/Portability.h>
+
+namespace folly {
+
+namespace detail { struct NoneHelper {}; }
+
+typedef int detail::NoneHelper::*None;
+
+const None none = nullptr;
+
+/**
+ * gcc-4.7 warns about use of uninitialized memory around the use of storage_
+ * even though this is explicitly initialized at each point.
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wuninitialized"
+# pragma GCC diagnostic ignored "-Wpragmas"
+# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif // __GNUC__
+
+class OptionalEmptyException : public std::runtime_error {
+ public:
+  OptionalEmptyException()
+      : std::runtime_error("Empty Optional cannot be unwrapped") {}
+};
+
+template<class Value>
+class Optional {
+ public:
+  static_assert(!std::is_reference<Value>::value,
+                "Optional may not be used with reference types");
+  static_assert(!std::is_abstract<Value>::value,
+                "Optional may not be used with abstract types");
+
+  Optional()
+    : hasValue_(false) {
+  }
+
+  Optional(const Optional& src)
+    noexcept(std::is_nothrow_copy_constructible<Value>::value) {
+
+    if (src.hasValue()) {
+      construct(src.value());
+    } else {
+      hasValue_ = false;
+    }
+  }
+
+  Optional(Optional&& src)
+    noexcept(std::is_nothrow_move_constructible<Value>::value) {
+
+    if (src.hasValue()) {
+      construct(std::move(src.value()));
+      src.clear();
+    } else {
+      hasValue_ = false;
+    }
+  }
+
+  /* implicit */ Optional(const None&) noexcept
+    : hasValue_(false) {
+  }
+
+  /* implicit */ Optional(Value&& newValue)
+    noexcept(std::is_nothrow_move_constructible<Value>::value) {
+    construct(std::move(newValue));
+  }
+
+  /* implicit */ Optional(const Value& newValue)
+    noexcept(std::is_nothrow_copy_constructible<Value>::value) {
+    construct(newValue);
+  }
+
+  ~Optional() noexcept {
+    clear();
+  }
+
+  void assign(const None&) {
+    clear();
+  }
+
+  void assign(Optional&& src) {
+    if (this != &src) {
+      if (src.hasValue()) {
+        assign(std::move(src.value()));
+        src.clear();
+      } else {
+        clear();
+      }
+    }
+  }
+
+  void assign(const Optional& src) {
+    if (src.hasValue()) {
+      assign(src.value());
+    } else {
+      clear();
+    }
+  }
+
+  void assign(Value&& newValue) {
+    if (hasValue()) {
+      value_ = std::move(newValue);
+    } else {
+      construct(std::move(newValue));
+    }
+  }
+
+  void assign(const Value& newValue) {
+    if (hasValue()) {
+      value_ = newValue;
+    } else {
+      construct(newValue);
+    }
+  }
+
+  template<class Arg>
+  Optional& operator=(Arg&& arg) {
+    assign(std::forward<Arg>(arg));
+    return *this;
+  }
+
+  Optional& operator=(Optional &&other)
+    noexcept (std::is_nothrow_move_assignable<Value>::value) {
+
+    assign(std::move(other));
+    return *this;
+  }
+
+  Optional& operator=(const Optional &other)
+    noexcept (std::is_nothrow_copy_assignable<Value>::value) {
+
+    assign(other);
+    return *this;
+  }
+
+  template<class... Args>
+  void emplace(Args&&... args) {
+    clear();
+    construct(std::forward<Args>(args)...);
+  }
+
+  void clear() {
+    if (hasValue()) {
+      hasValue_ = false;
+      value_.~Value();
+    }
+  }
+
+  const Value& value() const& {
+    require_value();
+    return value_;
+  }
+
+  Value& value() & {
+    require_value();
+    return value_;
+  }
+
+  Value value() && {
+    require_value();
+    return std::move(value_);
+  }
+
+  const Value* get_pointer() const&  { return hasValue_ ? &value_ : nullptr; }
+        Value* get_pointer()      &  { return hasValue_ ? &value_ : nullptr; }
+        Value* get_pointer()      && = delete;
+
+  bool hasValue() const { return hasValue_; }
+
+  explicit operator bool() const {
+    return hasValue();
+  }
+
+  const Value& operator*() const&  { return value(); }
+        Value& operator*()      &  { return value(); }
+        Value  operator*()      && { return std::move(value()); }
+
+  const Value* operator->() const { return &value(); }
+        Value* operator->()       { return &value(); }
+
+  // Return a copy of the value if set, or a given default if not.
+  template <class U>
+  Value value_or(U&& dflt) const& {
+    return hasValue_ ? value_ : std::forward<U>(dflt);
+  }
+
+  template <class U>
+  Value value_or(U&& dflt) && {
+    return hasValue_ ? std::move(value_) : std::forward<U>(dflt);
+  }
+
+ private:
+  void require_value() const {
+    if (!hasValue_) {
+      throw OptionalEmptyException();
+    }
+  }
+
+  template<class... Args>
+  void construct(Args&&... args) {
+    const void* ptr = &value_;
+    // for supporting const types
+    new(const_cast<void*>(ptr)) Value(std::forward<Args>(args)...);
+    hasValue_ = true;
+  }
+
+  // uninitialized
+  union { Value value_; };
+  bool hasValue_;
+};
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+template<class T>
+const T* get_pointer(const Optional<T>& opt) {
+  return opt.get_pointer();
+}
+
+template<class T>
+T* get_pointer(Optional<T>& opt) {
+  return opt.get_pointer();
+}
+
+template<class T>
+void swap(Optional<T>& a, Optional<T>& b) {
+  if (a.hasValue() && b.hasValue()) {
+    // both full
+    using std::swap;
+    swap(a.value(), b.value());
+  } else if (a.hasValue() || b.hasValue()) {
+    std::swap(a, b); // fall back to default implementation if they're mixed.
+  }
+}
+
+template<class T,
+         class Opt = Optional<typename std::decay<T>::type>>
+Opt make_optional(T&& v) {
+  return Opt(std::forward<T>(v));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Comparisons.
+
+template<class V>
+bool operator==(const Optional<V>& a, const V& b) {
+  return a.hasValue() && a.value() == b;
+}
+
+template<class V>
+bool operator!=(const Optional<V>& a, const V& b) {
+  return !(a == b);
+}
+
+template<class V>
+bool operator==(const V& a, const Optional<V>& b) {
+  return b.hasValue() && b.value() == a;
+}
+
+template<class V>
+bool operator!=(const V& a, const Optional<V>& b) {
+  return !(a == b);
+}
+
+template<class V>
+bool operator==(const Optional<V>& a, const Optional<V>& b) {
+  if (a.hasValue() != b.hasValue()) { return false; }
+  if (a.hasValue())                 { return a.value() == b.value(); }
+  return true;
+}
+
+template<class V>
+bool operator!=(const Optional<V>& a, const Optional<V>& b) {
+  return !(a == b);
+}
+
+template<class V>
+bool operator< (const Optional<V>& a, const Optional<V>& b) {
+  if (a.hasValue() != b.hasValue()) { return a.hasValue() < b.hasValue(); }
+  if (a.hasValue())                 { return a.value()    < b.value(); }
+  return false;
+}
+
+template<class V>
+bool operator> (const Optional<V>& a, const Optional<V>& b) {
+  return b < a;
+}
+
+template<class V>
+bool operator<=(const Optional<V>& a, const Optional<V>& b) {
+  return !(b < a);
+}
+
+template<class V>
+bool operator>=(const Optional<V>& a, const Optional<V>& b) {
+  return !(a < b);
+}
+
+// Suppress comparability of Optional<T> with T, despite implicit conversion.
+template<class V> bool operator< (const Optional<V>&, const V& other) = delete;
+template<class V> bool operator<=(const Optional<V>&, const V& other) = delete;
+template<class V> bool operator>=(const Optional<V>&, const V& other) = delete;
+template<class V> bool operator> (const Optional<V>&, const V& other) = delete;
+template<class V> bool operator< (const V& other, const Optional<V>&) = delete;
+template<class V> bool operator<=(const V& other, const Optional<V>&) = delete;
+template<class V> bool operator>=(const V& other, const Optional<V>&) = delete;
+template<class V> bool operator> (const V& other, const Optional<V>&) = delete;
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace folly
+
+#endif // FOLLY_OPTIONAL_H_
diff --git a/faux-folly/folly/Portability.h b/faux-folly/folly/Portability.h
new file mode 100644
index 0000000..77baf95
--- /dev/null
+++ b/faux-folly/folly/Portability.h
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_PORTABILITY_H_
+#define FOLLY_PORTABILITY_H_
+
+#include <cstddef>
+
+#ifndef FOLLY_NO_CONFIG
+#include <folly/folly-config.h>
+#endif
+
+#ifdef FOLLY_PLATFORM_CONFIG
+#include FOLLY_PLATFORM_CONFIG
+#endif
+
+#if FOLLY_HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <folly/CPortability.h>
+
+#ifdef __APPLE__
+# include <malloc/malloc.h>
+#endif
+
+#if FOLLY_HAVE_SCHED_H
+ #include <sched.h>
+ #ifndef FOLLY_HAVE_PTHREAD_YIELD
+  #define pthread_yield sched_yield
+ #endif
+#endif
+
+// A change in folly/MemoryMapping.cpp uses MAP_ANONYMOUS, which is named
+// MAP_ANON on OSX/BSD.
+#if defined(__APPLE__) || defined(__FreeBSD__)
+  #include <sys/mman.h>
+  #ifndef MAP_ANONYMOUS
+    #ifdef MAP_ANON
+      #define MAP_ANONYMOUS MAP_ANON
+    #endif
+  #endif
+#endif
+
+// compiler specific attribute translation
+// msvc should come first, so if clang is in msvc mode it gets the right defines
+
+#if defined(__clang__) || defined(__GNUC__)
+# define FOLLY_ALIGNED(size) __attribute__((__aligned__(size)))
+#elif defined(_MSC_VER)
+# define FOLLY_ALIGNED(size) __declspec(align(size))
+#else
+# error Cannot define FOLLY_ALIGNED on this platform
+#endif
+#define FOLLY_ALIGNED_MAX FOLLY_ALIGNED(alignof(std::max_align_t))
+
+// NOTE: this will only do checking in msvc with versions that support /analyze
+#if _MSC_VER
+# ifdef _USE_ATTRIBUTES_FOR_SAL
+#    undef _USE_ATTRIBUTES_FOR_SAL
+# endif
+/* nolint */
+# define _USE_ATTRIBUTES_FOR_SAL 1
+# include <sal.h>
+# define FOLLY_PRINTF_FORMAT _Printf_format_string_
+# define FOLLY_PRINTF_FORMAT_ATTR(format_param, dots_param) /**/
+#else
+# define FOLLY_PRINTF_FORMAT /**/
+# define FOLLY_PRINTF_FORMAT_ATTR(format_param, dots_param) \
+  __attribute__((__format__(__printf__, format_param, dots_param)))
+#endif
+
+// deprecated
+#if defined(__clang__) || defined(__GNUC__)
+# define FOLLY_DEPRECATED(msg) __attribute__((__deprecated__(msg)))
+#elif defined(_MSC_VER)
+# define FOLLY_DEPRECATED(msg) __declspec(deprecated(msg))
+#else
+# define FOLLY_DEPRECATED(msg)
+#endif
+
+// noreturn
+#if defined(_MSC_VER)
+# define FOLLY_NORETURN __declspec(noreturn)
+#elif defined(__clang__) || defined(__GNUC__)
+# define FOLLY_NORETURN __attribute__((__noreturn__))
+#else
+# define FOLLY_NORETURN
+#endif
+
+// noinline
+#ifdef _MSC_VER
+# define FOLLY_NOINLINE __declspec(noinline)
+#elif defined(__clang__) || defined(__GNUC__)
+# define FOLLY_NOINLINE __attribute__((__noinline__))
+#else
+# define FOLLY_NOINLINE
+#endif
+
+// always inline
+#ifdef _MSC_VER
+# define FOLLY_ALWAYS_INLINE __forceinline
+#elif defined(__clang__) || defined(__GNUC__)
+# define FOLLY_ALWAYS_INLINE inline __attribute__((__always_inline__))
+#else
+# define FOLLY_ALWAYS_INLINE
+#endif
+
+// detection for 64 bit
+#if defined(__x86_64__) || defined(_M_X64)
+# define FOLLY_X64 1
+#else
+# define FOLLY_X64 0
+#endif
+
+#if defined(__aarch64__)
+# define FOLLY_A64 1
+#else
+# define FOLLY_A64 0
+#endif
+
+// packing is very ugly in msvc
+#ifdef _MSC_VER
+# define FOLLY_PACK_ATTR /**/
+# define FOLLY_PACK_PUSH __pragma(pack(push, 1))
+# define FOLLY_PACK_POP __pragma(pack(pop))
+#elif defined(__clang__) || defined(__GNUC__)
+# define FOLLY_PACK_ATTR __attribute__((__packed__))
+# define FOLLY_PACK_PUSH /**/
+# define FOLLY_PACK_POP /**/
+#else
+# define FOLLY_PACK_ATTR /**/
+# define FOLLY_PACK_PUSH /**/
+# define FOLLY_PACK_POP /**/
+#endif
+
+// portable version check
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+/* nolint */
+#  define __GNUC_PREREQ(maj, min) ((__GNUC__ << 16) + __GNUC_MINOR__ >= \
+                                   ((maj) << 16) + (min))
+# else
+/* nolint */
+#  define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#if defined(__GNUC__) && !defined(__APPLE__) && !__GNUC_PREREQ(4,9)
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56019
+// gcc 4.8.x incorrectly placed max_align_t in the root namespace
+// Alias it into std (where it's found in 4.9 and later)
+namespace std { typedef ::max_align_t max_align_t; }
+#endif
+
+/* Define macro wrappers for C++11's "final" and "override" keywords, which
+ * are supported in gcc 4.7 but not gcc 4.6. */
+#if !defined(FOLLY_FINAL) && !defined(FOLLY_OVERRIDE)
+# if defined(__clang__) || __GNUC_PREREQ(4, 7)
+#  define FOLLY_FINAL final
+#  define FOLLY_OVERRIDE override
+# elif defined(_MSC_VER) && _MSC_VER >= 1600
+#  define FOLLY_FINAL final
+#  define FOLLY_OVERRIDE override
+# else
+#  define FOLLY_FINAL /**/
+#  define FOLLY_OVERRIDE /**/
+# endif
+#endif
+
+/* Platform specific TLS support
+ * gcc implements __thread
+ * msvc implements __declspec(thread)
+ * the semantics are the same
+ * (but remember __thread has different semantics when using emutls (ex. apple))
+ */
+#if defined(_MSC_VER)
+# define FOLLY_TLS __declspec(thread)
+#elif defined(__GNUC__) || defined(__clang__)
+# define FOLLY_TLS __thread
+#else
+# error cannot define platform specific thread local storage
+#endif
+
+// Define to 1 if you have the `preadv' and `pwritev' functions, respectively
+#if !defined(FOLLY_HAVE_PREADV) && !defined(FOLLY_HAVE_PWRITEV)
+# if defined(__GLIBC_PREREQ)
+#  if __GLIBC_PREREQ(2, 10)
+#   define FOLLY_HAVE_PREADV 1
+#   define FOLLY_HAVE_PWRITEV 1
+#  endif
+# endif
+#endif
+
+// It turns out that GNU libstdc++ and LLVM libc++ differ on how they implement
+// the 'std' namespace; the latter uses inline namepsaces. Wrap this decision
+// up in a macro to make forward-declarations easier.
+#if FOLLY_USE_LIBCPP
+#include <__config>
+#define FOLLY_NAMESPACE_STD_BEGIN     _LIBCPP_BEGIN_NAMESPACE_STD
+#define FOLLY_NAMESPACE_STD_END       _LIBCPP_END_NAMESPACE_STD
+#else
+#define FOLLY_NAMESPACE_STD_BEGIN     namespace std {
+#define FOLLY_NAMESPACE_STD_END       }
+#endif
+
+// Some platforms lack clock_gettime(2) and clock_getres(2). Inject our own
+// versions of these into the global namespace.
+#if FOLLY_HAVE_CLOCK_GETTIME
+#include <time.h>
+#else
+#include <folly/detail/Clock.h>
+#endif
+
+// Provide our own std::__throw_* wrappers for platforms that don't have them
+#if FOLLY_HAVE_BITS_FUNCTEXCEPT_H
+#include <bits/functexcept.h>
+#else
+#include <folly/detail/FunctionalExcept.h>
+#endif
+
+#if defined(__cplusplus)
+// Unfortunately, boost::has_trivial_copy<T> is broken in libc++ due to its
+// usage of __has_trivial_copy(), so we can't use it as a
+// least-common-denominator for C++11 implementations that don't support
+// std::is_trivially_copyable<T>.
+//
+//      http://stackoverflow.com/questions/12754886/has-trivial-copy-behaves-differently-in-clang-and-gcc-whos-right
+//
+// As a result, use std::is_trivially_copyable() where it exists, and fall back
+// to Boost otherwise.
+#if FOLLY_HAVE_STD__IS_TRIVIALLY_COPYABLE
+#include <type_traits>
+#define FOLLY_IS_TRIVIALLY_COPYABLE(T)                   \
+  (std::is_trivially_copyable<T>::value)
+#else
+#include <boost/type_traits.hpp>
+#define FOLLY_IS_TRIVIALLY_COPYABLE(T)                   \
+  (boost::has_trivial_copy<T>::value &&                  \
+   boost::has_trivial_destructor<T>::value)
+#endif
+#endif // __cplusplus
+
+// MSVC specific defines
+// mainly for posix compat
+#ifdef _MSC_VER
+
+// this definition is in a really silly place with a silly name
+// and ifdefing it every time we want it is painful
+#include <basetsd.h>
+typedef SSIZE_T ssize_t;
+
+// sprintf semantics are not exactly identical
+// but current usage is not a problem
+# define snprintf _snprintf
+
+// semantics here are identical
+# define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
+
+// compiler specific to compiler specific
+// nolint
+# define __PRETTY_FUNCTION__ __FUNCSIG__
+
+// Hide a GCC specific thing that breaks MSVC if left alone.
+# define __extension__
+
+#ifdef _M_IX86_FP
+# define FOLLY_SSE _M_IX86_FP
+# define FOLLY_SSE_MINOR 0
+#endif
+
+#endif
+
+#ifndef FOLLY_SSE
+# if defined(__SSE4_2__)
+#  define FOLLY_SSE 4
+#  define FOLLY_SSE_MINOR 2
+# elif defined(__SSE4_1__)
+#  define FOLLY_SSE 4
+#  define FOLLY_SSE_MINOR 1
+# elif defined(__SSE4__)
+#  define FOLLY_SSE 4
+#  define FOLLY_SSE_MINOR 0
+# elif defined(__SSE3__)
+#  define FOLLY_SSE 3
+#  define FOLLY_SSE_MINOR 0
+# elif defined(__SSE2__)
+#  define FOLLY_SSE 2
+#  define FOLLY_SSE_MINOR 0
+# elif defined(__SSE__)
+#  define FOLLY_SSE 1
+#  define FOLLY_SSE_MINOR 0
+# else
+#  define FOLLY_SSE 0
+#  define FOLLY_SSE_MINOR 0
+# endif
+#endif
+
+#if FOLLY_UNUSUAL_GFLAGS_NAMESPACE
+namespace FOLLY_GFLAGS_NAMESPACE { }
+namespace gflags {
+using namespace FOLLY_GFLAGS_NAMESPACE;
+}  // namespace gflags
+#endif
+
+// for TARGET_OS_IPHONE
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
+// MacOS doesn't have malloc_usable_size()
+#if defined(__APPLE__) && !defined(FOLLY_HAVE_MALLOC_USABLE_SIZE)
+inline size_t malloc_usable_size(void* ptr) {
+  return malloc_size(ptr);
+}
+#endif
+
+// RTTI may not be enabled for this compilation unit.
+#if defined(__GXX_RTTI) || defined(__cpp_rtti) || \
+    (defined(_MSC_VER) && defined(_CPPRTTI))
+# define FOLLY_HAS_RTTI 1
+#endif
+
+#ifdef _MSC_VER
+# include <intrin.h>
+#endif
+
+namespace folly {
+
+inline void asm_volatile_memory() {
+#if defined(__clang__) || defined(__GNUC__)
+  asm volatile("" : : : "memory");
+#elif defined(_MSC_VER)
+  ::_ReadWriteBarrier();
+#endif
+}
+
+inline void asm_volatile_pause() {
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+  ::_mm_pause();
+#elif defined(__i386__) || FOLLY_X64
+  asm volatile ("pause");
+#elif FOLLY_A64
+  asm volatile ("wfe");
+#endif
+}
+inline void asm_pause() {
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+  ::_mm_pause();
+#elif defined(__i386__) || FOLLY_X64
+  asm ("pause");
+#elif FOLLY_A64
+  asm ("wfe");
+#endif
+}
+
+}
+
+#endif // FOLLY_PORTABILITY_H_
diff --git a/faux-folly/folly/Preprocessor.h b/faux-folly/folly/Preprocessor.h
new file mode 100644
index 0000000..c753585
--- /dev/null
+++ b/faux-folly/folly/Preprocessor.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author: Andrei Alexandrescu
+
+#ifndef FOLLY_PREPROCESSOR_
+#define FOLLY_PREPROCESSOR_
+
+/**
+ * Necessarily evil preprocessor-related amenities.
+ */
+
+/**
+ * FB_ONE_OR_NONE(hello, world) expands to hello and
+ * FB_ONE_OR_NONE(hello) expands to nothing. This macro is used to
+ * insert or eliminate text based on the presence of another argument.
+ */
+#ifdef _MSC_VER
+
+#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
+#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 4, 3, 2, 1, 0)
+#define VARARG_IMPL2(base, count, ...) base##count(__VA_ARGS__)
+#define VARARG_IMPL(base, count, ...) VARARG_IMPL2(base, count, __VA_ARGS__)
+#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+#define FB_ONE_OR_NONE0() /* */
+#define FB_ONE_OR_NONE1(x) /* */
+#define FB_ONE_OR_NONE2(x, y) x
+#define FB_ONE_OR_NONE3(x, y, z) x
+#define FB_ONE_OR_NONE(...) VARARG(FB_ONE_OR_NONE, __VA_ARGS__)
+
+#else
+#define FB_ONE_OR_NONE(a, ...) FB_THIRD(a, ## __VA_ARGS__, a)
+#define FB_THIRD(a, b, ...) __VA_ARGS__
+#endif
+
+/**
+ * Helper macro that extracts the first argument out of a list of any
+ * number of arguments.
+ */
+#define FB_ARG_1(a, ...) a
+
+/**
+ * Helper macro that extracts the second argument out of a list of any
+ * number of arguments. If only one argument is given, it returns
+ * that.
+ */
+#define FB_ARG_2_OR_1(...) FB_ARG_2_OR_1_IMPL(__VA_ARGS__, __VA_ARGS__)
+// Support macro for the above
+#define FB_ARG_2_OR_1_IMPL(a, b, ...) b
+
+/**
+ * Helper macro that provides a way to pass argument with commas in it to
+ * some other macro whose syntax doesn't allow using extra parentheses.
+ * Example:
+ *
+ *   #define MACRO(type, name) type name
+ *   MACRO(FB_SINGLE_ARG(std::pair<size_t, size_t>), x);
+ *
+ */
+#define FB_SINGLE_ARG(...) __VA_ARGS__
+
+/**
+ * FB_ANONYMOUS_VARIABLE(str) introduces an identifier starting with
+ * str and ending with a number that varies with the line.
+ */
+#ifndef FB_ANONYMOUS_VARIABLE
+#define FB_CONCATENATE_IMPL(s1, s2) s1##s2
+#define FB_CONCATENATE(s1, s2) FB_CONCATENATE_IMPL(s1, s2)
+#ifdef __COUNTER__
+#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __COUNTER__)
+#else
+#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __LINE__)
+#endif
+#endif
+
+/**
+ * Use FB_STRINGIZE(x) when you'd want to do what #x does inside
+ * another macro expansion.
+ */
+#define FB_STRINGIZE(x) #x
+
+#endif // FOLLY_PREPROCESSOR_
diff --git a/faux-folly/folly/Random-inl.h b/faux-folly/folly/Random-inl.h
new file mode 100644
index 0000000..10da8d3
--- /dev/null
+++ b/faux-folly/folly/Random-inl.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_RANDOM_H_
+#error This file may only be included from folly/Random.h
+#endif
+
+namespace folly {
+
+namespace detail {
+
+// Return the state size needed by RNG, expressed as a number of uint32_t
+// integers. Specialized for all templates specified in the C++11 standard.
+// For some (mersenne_twister_engine), this is exported as a state_size static
+// data member; for others, the standard shows formulas.
+
+template <class RNG> struct StateSize {
+  // A sane default.
+  static constexpr size_t value = 512;
+};
+
+template <class RNG>
+constexpr size_t StateSize<RNG>::value;
+
+template <class UIntType, UIntType a, UIntType c, UIntType m>
+struct StateSize<std::linear_congruential_engine<UIntType, a, c, m>> {
+  // From the standard [rand.eng.lcong], this is ceil(log2(m) / 32) + 3,
+  // which is the same as ceil(ceil(log2(m) / 32) + 3, and
+  // ceil(log2(m)) <= std::numeric_limits<UIntType>::digits
+  static constexpr size_t value =
+    (std::numeric_limits<UIntType>::digits + 31) / 32 + 3;
+};
+
+template <class UIntType, UIntType a, UIntType c, UIntType m>
+constexpr size_t
+StateSize<std::linear_congruential_engine<UIntType, a, c, m>>::value;
+
+template <class UIntType, size_t w, size_t n, size_t m, size_t r,
+          UIntType a, size_t u, UIntType d, size_t s,
+          UIntType b, size_t t,
+          UIntType c, size_t l, UIntType f>
+struct StateSize<std::mersenne_twister_engine<UIntType, w, n, m, r,
+                                              a, u, d, s, b, t, c, l, f>> {
+  static constexpr size_t value =
+    std::mersenne_twister_engine<UIntType, w, n, m, r,
+                                 a, u, d, s, b, t, c, l, f>::state_size;
+};
+
+template <class UIntType, size_t w, size_t n, size_t m, size_t r,
+          UIntType a, size_t u, UIntType d, size_t s,
+          UIntType b, size_t t,
+          UIntType c, size_t l, UIntType f>
+constexpr size_t
+StateSize<std::mersenne_twister_engine<UIntType, w, n, m, r,
+                                       a, u, d, s, b, t, c, l, f>>::value;
+
+#if FOLLY_USE_SIMD_PRNG
+
+template <class UIntType, size_t m, size_t pos1, size_t sl1, size_t sl2,
+          size_t sr1, size_t sr2, uint32_t msk1, uint32_t msk2, uint32_t msk3,
+          uint32_t msk4, uint32_t parity1, uint32_t parity2, uint32_t parity3,
+          uint32_t parity4>
+struct StateSize<__gnu_cxx::simd_fast_mersenne_twister_engine<
+    UIntType, m, pos1, sl1, sl2, sr1, sr2, msk1, msk2, msk3, msk4,
+    parity1, parity2, parity3, parity4>> {
+  static constexpr size_t value =
+    __gnu_cxx::simd_fast_mersenne_twister_engine<
+        UIntType, m, pos1, sl1, sl2, sr1, sr2,
+        msk1, msk2, msk3, msk4,
+        parity1, parity2, parity3, parity4>::state_size;
+};
+
+template <class UIntType, size_t m, size_t pos1, size_t sl1, size_t sl2,
+          size_t sr1, size_t sr2, uint32_t msk1, uint32_t msk2, uint32_t msk3,
+          uint32_t msk4, uint32_t parity1, uint32_t parity2, uint32_t parity3,
+          uint32_t parity4>
+constexpr size_t
+StateSize<__gnu_cxx::simd_fast_mersenne_twister_engine<
+    UIntType, m, pos1, sl1, sl2, sr1, sr2, msk1, msk2, msk3, msk4,
+    parity1, parity2, parity3, parity4>>::value;
+
+#endif
+
+template <class UIntType, size_t w, size_t s, size_t r>
+struct StateSize<std::subtract_with_carry_engine<UIntType, w, s, r>> {
+  // [rand.eng.sub]: r * ceil(w / 32)
+  static constexpr size_t value = r * ((w + 31) / 32);
+};
+
+template <class UIntType, size_t w, size_t s, size_t r>
+constexpr size_t
+StateSize<std::subtract_with_carry_engine<UIntType, w, s, r>>::value;
+
+template <class RNG>
+struct SeedData {
+  SeedData() {
+    Random::secureRandom(seedData.data(), seedData.size() * sizeof(uint32_t));
+  }
+
+  static constexpr size_t stateSize = StateSize<RNG>::value;
+  std::array<uint32_t, stateSize> seedData;
+};
+
+}  // namespace detail
+
+template <class RNG>
+void Random::seed(ValidRNG<RNG>& rng) {
+  detail::SeedData<RNG> sd;
+  std::seed_seq s(std::begin(sd.seedData), std::end(sd.seedData));
+  rng.seed(s);
+}
+
+template <class RNG>
+auto Random::create() -> ValidRNG<RNG> {
+  detail::SeedData<RNG> sd;
+  std::seed_seq s(std::begin(sd.seedData), std::end(sd.seedData));
+  return RNG(s);
+}
+
+}  // namespaces
diff --git a/faux-folly/folly/Random.cpp b/faux-folly/folly/Random.cpp
new file mode 100644
index 0000000..62eb8f9
--- /dev/null
+++ b/faux-folly/folly/Random.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2015 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/Random.h>
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/time.h>
+#include <mutex>
+#include <random>
+#include <array>
+
+#include <glog/logging.h>
+#include <folly/File.h>
+#include <folly/FileUtil.h>
+
+#ifdef _MSC_VER
+# include <wincrypt.h>
+#endif
+
+namespace folly {
+
+namespace {
+
+void readRandomDevice(void* data, size_t size) {
+#ifdef _MSC_VER
+  static std::once_flag flag;
+  static HCRYPTPROV cryptoProv;
+  std::call_once(flag, [&] {
+    PCHECK(CryptAcquireContext(&cryptoProv, nullptr, nullptr,
+                               PROV_RSA_FULL, 0));
+  });
+  CHECK(size <= std::numeric_limits<DWORD>::max());
+  PCHECK(CryptGenRandom(cryptoProv, (DWORD)size, (BYTE*)data));
+#else
+  // Keep the random device open for the duration of the program.
+  static int randomFd = ::open("/dev/urandom", O_RDONLY);
+  PCHECK(randomFd >= 0);
+  auto bytesRead = readFull(randomFd, data, size);
+  PCHECK(bytesRead >= 0 && size_t(bytesRead) == size);
+#endif
+}
+
+class BufferedRandomDevice {
+ public:
+  static constexpr size_t kDefaultBufferSize = 128;
+
+  explicit BufferedRandomDevice(size_t bufferSize = kDefaultBufferSize);
+
+  void get(void* data, size_t size) {
+    if (LIKELY(size <= remaining())) {
+      memcpy(data, ptr_, size);
+      ptr_ += size;
+    } else {
+      getSlow(static_cast<unsigned char*>(data), size);
+    }
+  }
+
+ private:
+  void getSlow(unsigned char* data, size_t size);
+
+  inline size_t remaining() const {
+    return buffer_.get() + bufferSize_ - ptr_;
+  }
+
+  const size_t bufferSize_;
+  std::unique_ptr<unsigned char[]> buffer_;
+  unsigned char* ptr_;
+};
+
+BufferedRandomDevice::BufferedRandomDevice(size_t bufferSize)
+  : bufferSize_(bufferSize),
+    buffer_(new unsigned char[bufferSize]),
+    ptr_(buffer_.get() + bufferSize) {  // refill on first use
+}
+
+void BufferedRandomDevice::getSlow(unsigned char* data, size_t size) {
+  DCHECK_GT(size, remaining());
+  if (size >= bufferSize_) {
+    // Just read directly.
+    readRandomDevice(data, size);
+    return;
+  }
+
+  size_t copied = remaining();
+  memcpy(data, ptr_, copied);
+  data += copied;
+  size -= copied;
+
+  // refill
+  readRandomDevice(buffer_.get(), bufferSize_);
+  ptr_ = buffer_.get();
+
+  memcpy(data, ptr_, size);
+  ptr_ += size;
+}
+
+
+}  // namespace
+
+void Random::secureRandom(void* data, size_t size) {
+  static ThreadLocal<BufferedRandomDevice> bufferedRandomDevice;
+  bufferedRandomDevice->get(data, size);
+}
+
+ThreadLocalPRNG::ThreadLocalPRNG() {
+  static folly::ThreadLocal<ThreadLocalPRNG::LocalInstancePRNG> localInstance;
+  local_ = localInstance.get();
+}
+
+class ThreadLocalPRNG::LocalInstancePRNG {
+ public:
+  LocalInstancePRNG() : rng(Random::create()) { }
+
+  Random::DefaultGenerator rng;
+};
+
+uint32_t ThreadLocalPRNG::getImpl(LocalInstancePRNG* local) {
+  return local->rng();
+}
+
+}
diff --git a/faux-folly/folly/Random.h b/faux-folly/folly/Random.h
new file mode 100644
index 0000000..25ebf8e
--- /dev/null
+++ b/faux-folly/folly/Random.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_RANDOM_H_
+#define FOLLY_RANDOM_H_
+
+#include <type_traits>
+#include <random>
+#include <stdint.h>
+#include <folly/ThreadLocal.h>
+
+#if __GNUC_PREREQ(4, 8) && !defined(ANDROID)
+#include <ext/random>
+#define FOLLY_USE_SIMD_PRNG 1
+#endif
+
+namespace folly {
+
+/**
+ * A PRNG with one instance per thread. This PRNG uses a mersenne twister random
+ * number generator and is seeded from /dev/urandom. It should not be used for
+ * anything which requires security, only for statistical randomness.
+ *
+ * An instance of this class represents the current threads PRNG. This means
+ * copying an instance of this class across threads will result in corruption
+ *
+ * Most users will use the Random class which implicitly creates this class.
+ * However, if you are worried about performance, you can memoize the TLS
+ * lookups that get the per thread state by manually using this class:
+ *
+ * ThreadLocalPRNG rng = Random::threadLocalPRNG()
+ * for (...) {
+ *   Random::rand32(rng);
+ * }
+ */
+class ThreadLocalPRNG {
+ public:
+  typedef uint32_t result_type;
+
+  uint32_t operator()() {
+    // Using a static method allows the compiler to avoid allocating stack space
+    // for this class.
+    return getImpl(local_);
+  }
+
+  static constexpr result_type min() {
+    return std::numeric_limits<result_type>::min();
+  }
+  static constexpr result_type max() {
+    return std::numeric_limits<result_type>::max();
+  }
+  friend class Random;
+
+  ThreadLocalPRNG();
+
+ private:
+  class LocalInstancePRNG;
+
+  static result_type getImpl(LocalInstancePRNG* local);
+  LocalInstancePRNG* local_;
+};
+
+
+class Random {
+
+ private:
+  template<class RNG>
+  using ValidRNG = typename std::enable_if<
+   std::is_unsigned<typename std::result_of<RNG&()>::type>::value,
+   RNG>::type;
+
+ public:
+  // Default generator type.
+#if FOLLY_USE_SIMD_PRNG
+  typedef __gnu_cxx::sfmt19937 DefaultGenerator;
+#else
+  typedef std::mt19937 DefaultGenerator;
+#endif
+
+  /**
+   * Get secure random bytes. (On Linux and OSX, this means /dev/urandom).
+   */
+  static void secureRandom(void* data, size_t len);
+
+  /**
+   * Shortcut to get a secure random value of integral type.
+   */
+  template <class T>
+  static typename std::enable_if<
+    std::is_integral<T>::value && !std::is_same<T,bool>::value,
+    T>::type
+  secureRandom() {
+    T val;
+    secureRandom(&val, sizeof(val));
+    return val;
+  }
+
+  /**
+   * (Re-)Seed an existing RNG with a good seed.
+   *
+   * Note that you should usually use ThreadLocalPRNG unless you need
+   * reproducibility (such as during a test), in which case you'd want
+   * to create a RNG with a good seed in production, and seed it yourself
+   * in test.
+   */
+  template <class RNG = DefaultGenerator>
+  static void seed(ValidRNG<RNG>& rng);
+
+  /**
+   * Create a new RNG, seeded with a good seed.
+   *
+   * Note that you should usually use ThreadLocalPRNG unless you need
+   * reproducibility (such as during a test), in which case you'd want
+   * to create a RNG with a good seed in production, and seed it yourself
+   * in test.
+   */
+  template <class RNG = DefaultGenerator>
+  static ValidRNG<RNG> create();
+
+  /**
+   * Returns a random uint32_t
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static uint32_t rand32(ValidRNG<RNG> rng = RNG()) {
+    uint32_t r = rng.operator()();
+    return r;
+  }
+
+  /**
+   * Returns a random uint32_t in [0, max). If max == 0, returns 0.
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static uint32_t rand32(uint32_t max, ValidRNG<RNG> rng = RNG()) {
+    if (max == 0) {
+      return 0;
+    }
+
+    return std::uniform_int_distribution<uint32_t>(0, max - 1)(rng);
+  }
+
+  /**
+   * Returns a random uint32_t in [min, max). If min == max, returns 0.
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static uint32_t rand32(uint32_t min,
+                         uint32_t max,
+                         ValidRNG<RNG> rng = RNG()) {
+    if (min == max) {
+      return 0;
+    }
+
+    return std::uniform_int_distribution<uint32_t>(min, max - 1)(rng);
+  }
+
+  /**
+   * Returns a random uint64_t
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static uint64_t rand64(ValidRNG<RNG> rng = RNG()) {
+    return ((uint64_t) rng() << 32) | rng();
+  }
+
+  /**
+   * Returns a random uint64_t in [0, max). If max == 0, returns 0.
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static uint64_t rand64(uint64_t max, ValidRNG<RNG> rng = RNG()) {
+    if (max == 0) {
+      return 0;
+    }
+
+    return std::uniform_int_distribution<uint64_t>(0, max - 1)(rng);
+  }
+
+  /**
+   * Returns a random uint64_t in [min, max). If min == max, returns 0.
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static uint64_t rand64(uint64_t min,
+                         uint64_t max,
+                         ValidRNG<RNG> rng = RNG()) {
+    if (min == max) {
+      return 0;
+    }
+
+    return std::uniform_int_distribution<uint64_t>(min, max - 1)(rng);
+  }
+
+  /**
+   * Returns true 1/n of the time. If n == 0, always returns false
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static bool oneIn(uint32_t n, ValidRNG<RNG> rng = RNG()) {
+    if (n == 0) {
+      return false;
+    }
+
+    return rand32(n, rng) == 0;
+  }
+
+  /**
+   * Returns a double in [0, 1)
+   */
+  template<class RNG = ThreadLocalPRNG>
+  static double randDouble01(ValidRNG<RNG> rng = RNG()) {
+    return std::generate_canonical<double, std::numeric_limits<double>::digits>
+      (rng);
+  }
+
+  /**
+    * Returns a double in [min, max), if min == max, returns 0.
+    */
+  template<class RNG = ThreadLocalPRNG>
+  static double randDouble(double min, double max, ValidRNG<RNG> rng = RNG()) {
+    if (std::fabs(max - min) < std::numeric_limits<double>::epsilon()) {
+      return 0;
+    }
+    return std::uniform_real_distribution<double>(min, max)(rng);
+  }
+
+};
+
+/*
+ * Return a good seed for a random number generator.
+ * Note that this is a legacy function, as it returns a 32-bit value, which
+ * is too small to be useful as a "real" RNG seed. Use the functions in class
+ * Random instead.
+ */
+inline uint32_t randomNumberSeed() {
+  return Random::rand32();
+}
+
+}
+
+#include <folly/Random-inl.h>
+
+#endif
diff --git a/faux-folly/folly/Range.cpp b/faux-folly/folly/Range.cpp
new file mode 100644
index 0000000..bf4e28d
--- /dev/null
+++ b/faux-folly/folly/Range.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Mark Rabkin (mrabkin@fb.com)
+// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
+
+#include <folly/Range.h>
+
+#if FOLLY_HAVE_EMMINTRIN_H
+#include <emmintrin.h>  // __v16qi
+#endif
+#include <iostream>
+
+namespace folly {
+
+/**
+ * Predicates that can be used with qfind and startsWith
+ */
+const AsciiCaseSensitive asciiCaseSensitive = AsciiCaseSensitive();
+const AsciiCaseInsensitive asciiCaseInsensitive = AsciiCaseInsensitive();
+
+namespace {
+
+// It's okay if pages are bigger than this (as powers of two), but they should
+// not be smaller.
+constexpr size_t kMinPageSize = 4096;
+static_assert(kMinPageSize >= 16,
+              "kMinPageSize must be at least SSE register size");
+#define PAGE_FOR(addr) \
+  (reinterpret_cast<uintptr_t>(addr) / kMinPageSize)
+
+
+// Earlier versions of GCC (for example, Clang on Mac OS X, which is based on
+// GCC 4.2) do not have a full compliment of SSE builtins.
+#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
+inline size_t nextAlignedIndex(const char* arr) {
+   auto firstPossible = reinterpret_cast<uintptr_t>(arr) + 1;
+   return 1 +                       // add 1 because the index starts at 'arr'
+     ((firstPossible + 15) & ~0xF)  // round up to next multiple of 16
+     - firstPossible;
+}
+
+// build sse4.2-optimized version even if -msse4.2 is not passed to GCC
+size_t qfind_first_byte_of_needles16(const StringPiece haystack,
+                                     const StringPiece needles)
+  __attribute__ ((__target__("sse4.2"), noinline))
+  FOLLY_DISABLE_ADDRESS_SANITIZER;
+
+// helper method for case where needles.size() <= 16
+size_t qfind_first_byte_of_needles16(const StringPiece haystack,
+                                     const StringPiece needles) {
+  DCHECK(!haystack.empty());
+  DCHECK(!needles.empty());
+  DCHECK_LE(needles.size(), 16);
+  if ((needles.size() <= 2 && haystack.size() >= 256) ||
+      // must bail if we can't even SSE-load a single segment of haystack
+      (haystack.size() < 16 &&
+       PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 15)) ||
+      // can't load needles into SSE register if it could cross page boundary
+      PAGE_FOR(needles.end() - 1) != PAGE_FOR(needles.data() + 15)) {
+    return detail::qfind_first_byte_of_nosse(haystack, needles);
+  }
+
+  auto arr2 = __builtin_ia32_loaddqu(needles.data());
+  // do an unaligned load for first block of haystack
+  auto arr1 = __builtin_ia32_loaddqu(haystack.data());
+  auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(),
+                                           arr1, haystack.size(), 0);
+  if (index < 16) {
+    return index;
+  }
+
+  // Now, we can do aligned loads hereafter...
+  size_t i = nextAlignedIndex(haystack.data());
+  for (; i < haystack.size(); i+= 16) {
+    void* ptr1 = __builtin_assume_aligned(haystack.data() + i, 16);
+    auto arr1 = *reinterpret_cast<const __v16qi*>(ptr1);
+    auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(),
+                                             arr1, haystack.size() - i, 0);
+    if (index < 16) {
+      return i + index;
+    }
+  }
+  return StringPiece::npos;
+}
+#endif // FOLLY_HAVE_EMMINTRIN_H && GCC 4.6+
+
+// 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
+class FastByteSet {
+ public:
+  FastByteSet() : size_(0) { }  // no init of arrays required!
+
+  inline void add(uint8_t i) {
+    if (!contains(i)) {
+      dense_[size_] = i;
+      sparse_[i] = size_;
+      size_++;
+    }
+  }
+  inline bool contains(uint8_t i) const {
+    DCHECK_LE(size_, 256);
+    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_[256];
+  uint8_t dense_[256];
+};
+
+}  // namespace
+
+namespace detail {
+
+size_t qfind_first_byte_of_byteset(const StringPiece haystack,
+                                   const StringPiece needles) {
+  FastByteSet s;
+  for (auto needle: needles) {
+    s.add(needle);
+  }
+  for (size_t index = 0; index < haystack.size(); ++index) {
+    if (s.contains(haystack[index])) {
+      return index;
+    }
+  }
+  return StringPiece::npos;
+}
+
+#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
+
+template <bool HAYSTACK_ALIGNED>
+size_t scanHaystackBlock(const StringPiece haystack,
+                         const StringPiece needles,
+                         uint64_t idx)
+// inline is okay because it's only called from other sse4.2 functions
+  __attribute__ ((__target__("sse4.2")))
+// Turn off ASAN because the "arr2 = ..." assignment in the loop below reads
+// up to 15 bytes beyond end of the buffer in #needles#.  That is ok because
+// ptr2 is always 16-byte aligned, so the read can never span a page boundary.
+// Also, the extra data that may be read is never actually used.
+  FOLLY_DISABLE_ADDRESS_SANITIZER;
+
+// Scans a 16-byte block of haystack (starting at blockStartIdx) to find first
+// needle. If HAYSTACK_ALIGNED, then haystack must be 16byte aligned.
+// If !HAYSTACK_ALIGNED, then caller must ensure that it is safe to load the
+// block.
+template <bool HAYSTACK_ALIGNED>
+size_t scanHaystackBlock(const StringPiece haystack,
+                         const StringPiece needles,
+                         uint64_t blockStartIdx) {
+  DCHECK_GT(needles.size(), 16);  // should handled by *needles16() method
+  DCHECK(blockStartIdx + 16 <= haystack.size() ||
+         (PAGE_FOR(haystack.data() + blockStartIdx) ==
+          PAGE_FOR(haystack.data() + blockStartIdx + 15)));
+
+  __v16qi arr1;
+  if (HAYSTACK_ALIGNED) {
+    void* ptr1 = __builtin_assume_aligned(haystack.data() + blockStartIdx, 16);
+    arr1 = *reinterpret_cast<const __v16qi*>(ptr1);
+  } else {
+    arr1 = __builtin_ia32_loaddqu(haystack.data() + blockStartIdx);
+  }
+
+  // This load is safe because needles.size() >= 16
+  auto arr2 = __builtin_ia32_loaddqu(needles.data());
+  size_t b = __builtin_ia32_pcmpestri128(
+    arr2, 16, arr1, haystack.size() - blockStartIdx, 0);
+
+  size_t j = nextAlignedIndex(needles.data());
+  for (; j < needles.size(); j += 16) {
+    void* ptr2 = __builtin_assume_aligned(needles.data() + j, 16);
+    arr2 = *reinterpret_cast<const __v16qi*>(ptr2);
+
+    auto index = __builtin_ia32_pcmpestri128(
+      arr2, needles.size() - j, arr1, haystack.size() - blockStartIdx, 0);
+    b = std::min<size_t>(index, b);
+  }
+
+  if (b < 16) {
+    return blockStartIdx + b;
+  }
+  return StringPiece::npos;
+}
+
+size_t qfind_first_byte_of_sse42(const StringPiece haystack,
+                                 const StringPiece needles)
+  __attribute__ ((__target__("sse4.2"), noinline));
+
+size_t qfind_first_byte_of_sse42(const StringPiece haystack,
+                                 const StringPiece needles) {
+  if (UNLIKELY(needles.empty() || haystack.empty())) {
+    return StringPiece::npos;
+  } else if (needles.size() <= 16) {
+    // we can save some unnecessary load instructions by optimizing for
+    // the common case of needles.size() <= 16
+    return qfind_first_byte_of_needles16(haystack, needles);
+  }
+
+  if (haystack.size() < 16 &&
+      PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 16)) {
+    // We can't safely SSE-load haystack. Use a different approach.
+    if (haystack.size() <= 2) {
+      return qfind_first_of(haystack, needles, asciiCaseSensitive);
+    }
+    return qfind_first_byte_of_byteset(haystack, needles);
+  }
+
+  auto ret = scanHaystackBlock<false>(haystack, needles, 0);
+  if (ret != StringPiece::npos) {
+    return ret;
+  }
+
+  size_t i = nextAlignedIndex(haystack.data());
+  for (; i < haystack.size(); i += 16) {
+    auto ret = scanHaystackBlock<true>(haystack, needles, i);
+    if (ret != StringPiece::npos) {
+      return ret;
+    }
+  }
+
+  return StringPiece::npos;
+}
+#endif // FOLLY_HAVE_EMMINTRIN_H && GCC 4.6+
+
+size_t qfind_first_byte_of_nosse(const StringPiece haystack,
+                                 const StringPiece needles) {
+  if (UNLIKELY(needles.empty() || haystack.empty())) {
+    return StringPiece::npos;
+  }
+  // The thresholds below were empirically determined by benchmarking.
+  // This is not an exact science since it depends on the CPU, the size of
+  // needles, and the size of haystack.
+  if ((needles.size() >= 4 && haystack.size() <= 10) ||
+             (needles.size() >= 16 && haystack.size() <= 64) ||
+             needles.size() >= 32) {
+    return qfind_first_byte_of_byteset(haystack, needles);
+  }
+  return qfind_first_of(haystack, needles, asciiCaseSensitive);
+}
+
+}  // namespace detail
+}  // namespace folly
diff --git a/faux-folly/folly/Range.h b/faux-folly/folly/Range.h
new file mode 100644
index 0000000..4a06619
--- /dev/null
+++ b/faux-folly/folly/Range.h
@@ -0,0 +1,1151 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Mark Rabkin (mrabkin@fb.com)
+// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
+
+#ifndef FOLLY_RANGE_H_
+#define FOLLY_RANGE_H_
+
+#include <folly/Portability.h>
+#include <folly/FBString.h>
+#include <folly/SpookyHashV2.h>
+
+#include <algorithm>
+#include <boost/operators.hpp>
+#include <climits>
+#include <cstring>
+#include <glog/logging.h>
+#include <iosfwd>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+
+// libc++ doesn't provide this header, nor does msvc
+#ifdef FOLLY_HAVE_BITS_CXXCONFIG_H
+// This file appears in two locations: inside fbcode and in the
+// libstdc++ source code (when embedding fbstring as std::string).
+// To aid in this schizophrenic use, two macros are defined in
+// c++config.h:
+//   _LIBSTDCXX_FBSTRING - Set inside libstdc++.  This is useful to
+//      gate use inside fbcode v. libstdc++
+#include <bits/c++config.h>
+#endif
+
+#include <folly/Traits.h>
+#include <folly/Likely.h>
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
+namespace folly {
+
+template <class T> class Range;
+
+/**
+ * Finds the first occurrence of needle in haystack. The algorithm is on
+ * average faster than O(haystack.size() * needle.size()) but not as fast
+ * as Boyer-Moore. On the upside, it does not do any upfront
+ * preprocessing and does not allocate memory.
+ */
+template <class T, class Comp = std::equal_to<typename Range<T>::value_type>>
+inline size_t qfind(const Range<T> & haystack,
+                    const Range<T> & needle,
+                    Comp eq = Comp());
+
+/**
+ * Finds the first occurrence of needle in haystack. The result is the
+ * offset reported to the beginning of haystack, or string::npos if
+ * needle wasn't found.
+ */
+template <class T>
+size_t qfind(const Range<T> & haystack,
+             const typename Range<T>::value_type& needle);
+
+/**
+ * Finds the last occurrence of needle in haystack. The result is the
+ * offset reported to the beginning of haystack, or string::npos if
+ * needle wasn't found.
+ */
+template <class T>
+size_t rfind(const Range<T> & haystack,
+             const typename Range<T>::value_type& needle);
+
+
+/**
+ * Finds the first occurrence of any element of needle in
+ * haystack. The algorithm is O(haystack.size() * needle.size()).
+ */
+template <class T>
+inline size_t qfind_first_of(const Range<T> & haystack,
+                             const Range<T> & needle);
+
+/**
+ * Small internal helper - returns the value just before an iterator.
+ */
+namespace detail {
+
+/**
+ * For random-access iterators, the value before is simply i[-1].
+ */
+template <class Iter>
+typename std::enable_if<
+  std::is_same<typename std::iterator_traits<Iter>::iterator_category,
+               std::random_access_iterator_tag>::value,
+  typename std::iterator_traits<Iter>::reference>::type
+value_before(Iter i) {
+  return i[-1];
+}
+
+/**
+ * For all other iterators, we need to use the decrement operator.
+ */
+template <class Iter>
+typename std::enable_if<
+  !std::is_same<typename std::iterator_traits<Iter>::iterator_category,
+                std::random_access_iterator_tag>::value,
+  typename std::iterator_traits<Iter>::reference>::type
+value_before(Iter i) {
+  return *--i;
+}
+
+/*
+ * Use IsCharPointer<T>::type to enable const char* or char*.
+ * Use IsCharPointer<T>::const_type to enable only const char*.
+ */
+template <class T> struct IsCharPointer {};
+
+template <>
+struct IsCharPointer<char*> {
+  typedef int type;
+};
+
+template <>
+struct IsCharPointer<const char*> {
+  typedef int const_type;
+  typedef int type;
+};
+
+} // namespace detail
+
+/**
+ * Range abstraction keeping a pair of iterators. We couldn't use
+ * boost's similar range abstraction because we need an API identical
+ * with the former StringPiece class, which is used by a lot of other
+ * code. This abstraction does fulfill the needs of boost's
+ * range-oriented algorithms though.
+ *
+ * (Keep memory lifetime in mind when using this class, since it
+ * doesn't manage the data it refers to - just like an iterator
+ * wouldn't.)
+ */
+template <class Iter>
+class Range : private boost::totally_ordered<Range<Iter> > {
+public:
+  typedef std::size_t size_type;
+  typedef Iter iterator;
+  typedef Iter const_iterator;
+  typedef typename std::remove_reference<
+    typename std::iterator_traits<Iter>::reference>::type
+  value_type;
+  typedef typename std::iterator_traits<Iter>::reference reference;
+
+  /**
+   * For MutableStringPiece and MutableByteRange we define StringPiece
+   * and ByteRange as const_range_type (for everything else its just
+   * identity). We do that to enable operations such as find with
+   * args which are const.
+   */
+  typedef typename std::conditional<
+    std::is_same<Iter, char*>::value
+      || std::is_same<Iter, unsigned char*>::value,
+    Range<const value_type*>,
+    Range<Iter>>::type const_range_type;
+
+  typedef std::char_traits<typename std::remove_const<value_type>::type>
+    traits_type;
+
+  static const size_type npos;
+
+  // Works for all iterators
+  constexpr Range() : b_(), e_() {
+  }
+
+  constexpr Range(const Range&) = default;
+  constexpr Range(Range&&) = default;
+
+public:
+  // Works for all iterators
+  constexpr Range(Iter start, Iter end) : b_(start), e_(end) {
+  }
+
+  // Works only for random-access iterators
+  constexpr Range(Iter start, size_t size)
+      : b_(start), e_(start + size) { }
+
+#if FOLLY_HAVE_CONSTEXPR_STRLEN
+  template <class T = Iter, typename detail::IsCharPointer<T>::type = 0>
+  constexpr /* implicit */ Range(Iter str)
+      : b_(str), e_(str + strlen(str)) {}
+#else
+  template <class T = Iter, typename detail::IsCharPointer<T>::type = 0>
+  /* implicit */ Range(Iter str)
+      : b_(str), e_(str + strlen(str)) {}
+#endif
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  /* implicit */ Range(const std::string& str)
+      : b_(str.data()), e_(b_ + str.size()) {}
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  Range(const std::string& str, std::string::size_type startFrom) {
+    if (UNLIKELY(startFrom > str.size())) {
+      throw std::out_of_range("index out of range");
+    }
+    b_ = str.data() + startFrom;
+    e_ = str.data() + str.size();
+  }
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  Range(const std::string& str,
+        std::string::size_type startFrom,
+        std::string::size_type size) {
+    if (UNLIKELY(startFrom > str.size())) {
+      throw std::out_of_range("index out of range");
+    }
+    b_ = str.data() + startFrom;
+    if (str.size() - startFrom < size) {
+      e_ = str.data() + str.size();
+    } else {
+      e_ = b_ + size;
+    }
+  }
+
+  Range(const Range& other,
+        size_type first,
+        size_type length = npos)
+      : Range(other.subpiece(first, length))
+    { }
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  /* implicit */ Range(const fbstring& str)
+    : b_(str.data()), e_(b_ + str.size()) { }
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  Range(const fbstring& str, fbstring::size_type startFrom) {
+    if (UNLIKELY(startFrom > str.size())) {
+      throw std::out_of_range("index out of range");
+    }
+    b_ = str.data() + startFrom;
+    e_ = str.data() + str.size();
+  }
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  Range(const fbstring& str, fbstring::size_type startFrom,
+        fbstring::size_type size) {
+    if (UNLIKELY(startFrom > str.size())) {
+      throw std::out_of_range("index out of range");
+    }
+    b_ = str.data() + startFrom;
+    if (str.size() - startFrom < size) {
+      e_ = str.data() + str.size();
+    } else {
+      e_ = b_ + size;
+    }
+  }
+
+  // Allow implicit conversion from Range<const char*> (aka StringPiece) to
+  // Range<const unsigned char*> (aka ByteRange), as they're both frequently
+  // used to represent ranges of bytes.  Allow explicit conversion in the other
+  // direction.
+  template <class OtherIter, typename std::enable_if<
+      (std::is_same<Iter, const unsigned char*>::value &&
+       (std::is_same<OtherIter, const char*>::value ||
+        std::is_same<OtherIter, char*>::value)), int>::type = 0>
+  /* implicit */ Range(const Range<OtherIter>& other)
+    : b_(reinterpret_cast<const unsigned char*>(other.begin())),
+      e_(reinterpret_cast<const unsigned char*>(other.end())) {
+  }
+
+  template <class OtherIter, typename std::enable_if<
+      (std::is_same<Iter, unsigned char*>::value &&
+       std::is_same<OtherIter, char*>::value), int>::type = 0>
+  /* implicit */ Range(const Range<OtherIter>& other)
+    : b_(reinterpret_cast<unsigned char*>(other.begin())),
+      e_(reinterpret_cast<unsigned char*>(other.end())) {
+  }
+
+  template <class OtherIter, typename std::enable_if<
+      (std::is_same<Iter, const char*>::value &&
+       (std::is_same<OtherIter, const unsigned char*>::value ||
+        std::is_same<OtherIter, unsigned char*>::value)), int>::type = 0>
+  explicit Range(const Range<OtherIter>& other)
+    : b_(reinterpret_cast<const char*>(other.begin())),
+      e_(reinterpret_cast<const char*>(other.end())) {
+  }
+
+  template <class OtherIter, typename std::enable_if<
+      (std::is_same<Iter, char*>::value &&
+       std::is_same<OtherIter, unsigned char*>::value), int>::type = 0>
+  explicit Range(const Range<OtherIter>& other)
+    : b_(reinterpret_cast<char*>(other.begin())),
+      e_(reinterpret_cast<char*>(other.end())) {
+  }
+
+  // Allow implicit conversion from Range<From> to Range<To> if From is
+  // implicitly convertible to To.
+  template <class OtherIter, typename std::enable_if<
+     (!std::is_same<Iter, OtherIter>::value &&
+      std::is_convertible<OtherIter, Iter>::value), int>::type = 0>
+  constexpr /* implicit */ Range(const Range<OtherIter>& other)
+    : b_(other.begin()),
+      e_(other.end()) {
+  }
+
+  // Allow explicit conversion from Range<From> to Range<To> if From is
+  // explicitly convertible to To.
+  template <class OtherIter, typename std::enable_if<
+    (!std::is_same<Iter, OtherIter>::value &&
+     !std::is_convertible<OtherIter, Iter>::value &&
+     std::is_constructible<Iter, const OtherIter&>::value), int>::type = 0>
+  constexpr explicit Range(const Range<OtherIter>& other)
+    : b_(other.begin()),
+      e_(other.end()) {
+  }
+
+  Range& operator=(const Range& rhs) & = default;
+  Range& operator=(Range&& rhs) & = default;
+
+  void clear() {
+    b_ = Iter();
+    e_ = Iter();
+  }
+
+  void assign(Iter start, Iter end) {
+    b_ = start;
+    e_ = end;
+  }
+
+  void reset(Iter start, size_type size) {
+    b_ = start;
+    e_ = start + size;
+  }
+
+  // Works only for Range<const char*>
+  void reset(const std::string& str) {
+    reset(str.data(), str.size());
+  }
+
+  size_type size() const {
+    assert(b_ <= e_);
+    return e_ - b_;
+  }
+  size_type walk_size() const {
+    assert(b_ <= e_);
+    return std::distance(b_, e_);
+  }
+  bool empty() const { return b_ == e_; }
+  Iter data() const { return b_; }
+  Iter start() const { return b_; }
+  Iter begin() const { return b_; }
+  Iter end() const { return e_; }
+  Iter cbegin() const { return b_; }
+  Iter cend() const { return e_; }
+  value_type& front() {
+    assert(b_ < e_);
+    return *b_;
+  }
+  value_type& back() {
+    assert(b_ < e_);
+    return detail::value_before(e_);
+  }
+  const value_type& front() const {
+    assert(b_ < e_);
+    return *b_;
+  }
+  const value_type& back() const {
+    assert(b_ < e_);
+    return detail::value_before(e_);
+  }
+  // Works only for Range<const char*> and Range<char*>
+  std::string str() const { return std::string(b_, size()); }
+  std::string toString() const { return str(); }
+  // Works only for Range<const char*> and Range<char*>
+  fbstring fbstr() const { return fbstring(b_, size()); }
+  fbstring toFbstring() const { return fbstr(); }
+
+  const_range_type castToConst() const {
+    return const_range_type(*this);
+  };
+
+  // Works only for Range<const char*> and Range<char*>
+  int compare(const const_range_type& o) const {
+    const size_type tsize = this->size();
+    const size_type osize = o.size();
+    const size_type msize = std::min(tsize, osize);
+    int r = traits_type::compare(data(), o.data(), msize);
+    if (r == 0 && tsize != osize) {
+      // We check the signed bit of the subtraction and bit shift it
+      // to produce either 0 or 2. The subtraction yields the
+      // comparison values of either -1 or 1.
+      r = (static_cast<int>(
+             (osize - tsize) >> (CHAR_BIT * sizeof(size_t) - 1)) << 1) - 1;
+    }
+    return r;
+  }
+
+  value_type& operator[](size_t i) {
+    DCHECK_GT(size(), i);
+    return b_[i];
+  }
+
+  const value_type& operator[](size_t i) const {
+    DCHECK_GT(size(), i);
+    return b_[i];
+  }
+
+  value_type& at(size_t i) {
+    if (i >= size()) throw std::out_of_range("index out of range");
+    return b_[i];
+  }
+
+  const value_type& at(size_t i) const {
+    if (i >= size()) throw std::out_of_range("index out of range");
+    return b_[i];
+  }
+
+  // Do NOT use this function, which was left behind for backwards
+  // compatibility.  Use SpookyHashV2 instead -- it is faster, and produces
+  // a 64-bit hash, which means dramatically fewer collisions in large maps.
+  // (The above advice does not apply if you are targeting a 32-bit system.)
+  //
+  // Works only for Range<const char*> and Range<char*>
+  uint32_t hash() const {
+    // Taken from fbi/nstring.h:
+    //    Quick and dirty bernstein hash...fine for short ascii strings
+    uint32_t hash = 5381;
+    for (size_t ix = 0; ix < size(); ix++) {
+      hash = ((hash << 5) + hash) + b_[ix];
+    }
+    return hash;
+  }
+
+  void advance(size_type n) {
+    if (UNLIKELY(n > size())) {
+      throw std::out_of_range("index out of range");
+    }
+    b_ += n;
+  }
+
+  void subtract(size_type n) {
+    if (UNLIKELY(n > size())) {
+      throw std::out_of_range("index out of range");
+    }
+    e_ -= n;
+  }
+
+  void pop_front() {
+    assert(b_ < e_);
+    ++b_;
+  }
+
+  void pop_back() {
+    assert(b_ < e_);
+    --e_;
+  }
+
+  Range subpiece(size_type first, size_type length = npos) const {
+    if (UNLIKELY(first > size())) {
+      throw std::out_of_range("index out of range");
+    }
+
+    return Range(b_ + first, std::min(length, size() - first));
+  }
+
+  // string work-alike functions
+  size_type find(const_range_type str) const {
+    return qfind(castToConst(), str);
+  }
+
+  size_type find(const_range_type str, size_t pos) const {
+    if (pos > size()) return std::string::npos;
+    size_t ret = qfind(castToConst().subpiece(pos), str);
+    return ret == npos ? ret : ret + pos;
+  }
+
+  size_type find(Iter s, size_t pos, size_t n) const {
+    if (pos > size()) return std::string::npos;
+    auto forFinding = castToConst();
+    size_t ret = qfind(
+        pos ? forFinding.subpiece(pos) : forFinding, const_range_type(s, n));
+    return ret == npos ? ret : ret + pos;
+  }
+
+  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor
+  size_type find(const Iter s) const {
+    return qfind(castToConst(), const_range_type(s));
+  }
+
+  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor
+  size_type find(const Iter s, size_t pos) const {
+    if (pos > size()) return std::string::npos;
+    size_type ret = qfind(castToConst().subpiece(pos), const_range_type(s));
+    return ret == npos ? ret : ret + pos;
+  }
+
+  size_type find(value_type c) const {
+    return qfind(castToConst(), c);
+  }
+
+  size_type rfind(value_type c) const {
+    return folly::rfind(castToConst(), c);
+  }
+
+  size_type find(value_type c, size_t pos) const {
+    if (pos > size()) return std::string::npos;
+    size_type ret = qfind(castToConst().subpiece(pos), c);
+    return ret == npos ? ret : ret + pos;
+  }
+
+  size_type find_first_of(const_range_type needles) const {
+    return qfind_first_of(castToConst(), needles);
+  }
+
+  size_type find_first_of(const_range_type needles, size_t pos) const {
+    if (pos > size()) return std::string::npos;
+    size_type ret = qfind_first_of(castToConst().subpiece(pos), needles);
+    return ret == npos ? ret : ret + pos;
+  }
+
+  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor
+  size_type find_first_of(Iter needles) const {
+    return find_first_of(const_range_type(needles));
+  }
+
+  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor
+  size_type find_first_of(Iter needles, size_t pos) const {
+    return find_first_of(const_range_type(needles), pos);
+  }
+
+  size_type find_first_of(Iter needles, size_t pos, size_t n) const {
+    return find_first_of(const_range_type(needles, n), pos);
+  }
+
+  size_type find_first_of(value_type c) const {
+    return find(c);
+  }
+
+  size_type find_first_of(value_type c, size_t pos) const {
+    return find(c, pos);
+  }
+
+  /**
+   * Determine whether the range contains the given subrange or item.
+   *
+   * Note: Call find() directly if the index is needed.
+   */
+  bool contains(const const_range_type& other) const {
+    return find(other) != std::string::npos;
+  }
+
+  bool contains(const value_type& other) const {
+    return find(other) != std::string::npos;
+  }
+
+  void swap(Range& rhs) {
+    std::swap(b_, rhs.b_);
+    std::swap(e_, rhs.e_);
+  }
+
+  /**
+   * Does this Range start with another range?
+   */
+  bool startsWith(const const_range_type& other) const {
+    return size() >= other.size()
+      && castToConst().subpiece(0, other.size()) == other;
+  }
+  bool startsWith(value_type c) const {
+    return !empty() && front() == c;
+  }
+
+  /**
+   * Does this Range end with another range?
+   */
+  bool endsWith(const const_range_type& other) const {
+    return size() >= other.size()
+      && castToConst().subpiece(size() - other.size()) == other;
+  }
+  bool endsWith(value_type c) const {
+    return !empty() && back() == c;
+  }
+
+  /**
+   * Remove the given prefix and return true if the range starts with the given
+   * prefix; return false otherwise.
+   */
+  bool removePrefix(const const_range_type& prefix) {
+    return startsWith(prefix) && (b_ += prefix.size(), true);
+  }
+  bool removePrefix(value_type prefix) {
+    return startsWith(prefix) && (++b_, true);
+  }
+
+  /**
+   * Remove the given suffix and return true if the range ends with the given
+   * suffix; return false otherwise.
+   */
+  bool removeSuffix(const const_range_type& suffix) {
+    return endsWith(suffix) && (e_ -= suffix.size(), true);
+  }
+  bool removeSuffix(value_type suffix) {
+    return endsWith(suffix) && (--e_, true);
+  }
+
+  /**
+   * Replaces the content of the range, starting at position 'pos', with
+   * contents of 'replacement'. Entire 'replacement' must fit into the
+   * range. Returns false if 'replacements' does not fit. Example use:
+   *
+   * char in[] = "buffer";
+   * auto msp = MutablesStringPiece(input);
+   * EXPECT_TRUE(msp.replaceAt(2, "tt"));
+   * EXPECT_EQ(msp, "butter");
+   *
+   * // not enough space
+   * EXPECT_FALSE(msp.replace(msp.size() - 1, "rr"));
+   * EXPECT_EQ(msp, "butter"); // unchanged
+   */
+  bool replaceAt(size_t pos, const_range_type replacement) {
+    if (size() < pos + replacement.size()) {
+      return false;
+    }
+
+    std::copy(replacement.begin(), replacement.end(), begin() + pos);
+
+    return true;
+  }
+
+  /**
+   * Replaces all occurences of 'source' with 'dest'. Returns number
+   * of replacements made. Source and dest have to have the same
+   * length. Throws if the lengths are different. If 'source' is a
+   * pattern that is overlapping with itself, we perform sequential
+   * replacement: "aaaaaaa".replaceAll("aa", "ba") --> "bababaa"
+   *
+   * Example use:
+   *
+   * char in[] = "buffer";
+   * auto msp = MutablesStringPiece(input);
+   * EXPECT_EQ(msp.replaceAll("ff","tt"), 1);
+   * EXPECT_EQ(msp, "butter");
+   */
+  size_t replaceAll(const_range_type source, const_range_type dest) {
+    if (source.size() != dest.size()) {
+      throw std::invalid_argument(
+          "replacement must have the same size as source");
+    }
+
+    if (dest.empty()) {
+      return 0;
+    }
+
+    size_t pos = 0;
+    size_t num_replaced = 0;
+    size_type found = std::string::npos;
+    while ((found = find(source, pos)) != std::string::npos) {
+      replaceAt(found, dest);
+      pos += source.size();
+      ++num_replaced;
+    }
+
+    return num_replaced;
+  }
+
+  /**
+   * Splits this `Range` `[b, e)` in the position `i` dictated by the next
+   * occurence of `delimiter`.
+   *
+   * Returns a new `Range` `[b, i)` and adjusts this range to start right after
+   * the delimiter's position. This range will be empty if the delimiter is not
+   * found. If called on an empty `Range`, both this and the returned `Range`
+   * will be empty.
+   *
+   * Example:
+   *
+   *  folly::StringPiece s("sample string for split_next");
+   *  auto p = s.split_step(' ');
+   *
+   *  // prints "string for split_next"
+   *  cout << s << endl;
+   *
+   *  // prints "sample"
+   *  cout << p << endl;
+   *
+   * Example 2:
+   *
+   *  void tokenize(StringPiece s, char delimiter) {
+   *    while (!s.empty()) {
+   *      cout << s.split_step(delimiter);
+   *    }
+   *  }
+   *
+   * @author: Marcelo Juchem <marcelo@fb.com>
+   */
+  Range split_step(value_type delimiter) {
+    auto i = std::find(b_, e_, delimiter);
+    Range result(b_, i);
+
+    b_ = i == e_ ? e_ : std::next(i);
+
+    return result;
+  }
+
+  Range split_step(Range delimiter) {
+    auto i = find(delimiter);
+    Range result(b_, i == std::string::npos ? size() : i);
+
+    b_ = result.end() == e_ ? e_ : std::next(result.end(), delimiter.size());
+
+    return result;
+  }
+
+  /**
+   * Convenience method that calls `split_step()` and passes the result to a
+   * functor, returning whatever the functor does. Any additional arguments
+   * `args` passed to this function are perfectly forwarded to the functor.
+   *
+   * Say you have a functor with this signature:
+   *
+   *  Foo fn(Range r) { }
+   *
+   * `split_step()`'s return type will be `Foo`. It works just like:
+   *
+   *  auto result = fn(myRange.split_step(' '));
+   *
+   * A functor returning `void` is also supported.
+   *
+   * Example:
+   *
+   *  void do_some_parsing(folly::StringPiece s) {
+   *    auto version = s.split_step(' ', [&](folly::StringPiece x) {
+   *      if (x.empty()) {
+   *        throw std::invalid_argument("empty string");
+   *      }
+   *      return std::strtoull(x.begin(), x.end(), 16);
+   *    });
+   *
+   *    // ...
+   *  }
+   *
+   *  struct Foo {
+   *    void parse(folly::StringPiece s) {
+   *      s.split_step(' ', parse_field, bar, 10);
+   *      s.split_step('\t', parse_field, baz, 20);
+   *
+   *      auto const kludge = [](folly::StringPiece x, int &out, int def) {
+   *        if (x == "null") {
+   *          out = 0;
+   *        } else {
+   *          parse_field(x, out, def);
+   *        }
+   *      };
+   *
+   *      s.split_step('\t', kludge, gaz);
+   *      s.split_step(' ', kludge, foo);
+   *    }
+   *
+   *  private:
+   *    int bar;
+   *    int baz;
+   *    int gaz;
+   *    int foo;
+   *
+   *    static parse_field(folly::StringPiece s, int &out, int def) {
+   *      try {
+   *        out = folly::to<int>(s);
+   *      } catch (std::exception const &) {
+   *        value = def;
+   *      }
+   *    }
+   *  };
+   *
+   * @author: Marcelo Juchem <marcelo@fb.com>
+   */
+  template <typename TProcess, typename... Args>
+  auto split_step(value_type delimiter, TProcess &&process, Args &&...args)
+    -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...))
+  { return process(split_step(delimiter), std::forward<Args>(args)...); }
+
+  template <typename TProcess, typename... Args>
+  auto split_step(Range delimiter, TProcess &&process, Args &&...args)
+    -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...))
+  { return process(split_step(delimiter), std::forward<Args>(args)...); }
+
+private:
+  Iter b_, e_;
+};
+
+template <class Iter>
+const typename Range<Iter>::size_type Range<Iter>::npos = std::string::npos;
+
+template <class T>
+void swap(Range<T>& lhs, Range<T>& rhs) {
+  lhs.swap(rhs);
+}
+
+/**
+ * Create a range from two iterators, with type deduction.
+ */
+template <class Iter>
+Range<Iter> range(Iter first, Iter last) {
+  return Range<Iter>(first, last);
+}
+
+/*
+ * Creates a range to reference the contents of a contiguous-storage container.
+ */
+// Use pointers for types with '.data()' member
+template <class Collection,
+          class T = typename std::remove_pointer<
+              decltype(std::declval<Collection>().data())>::type>
+Range<T*> range(Collection&& v) {
+  return Range<T*>(v.data(), v.data() + v.size());
+}
+
+template <class T, size_t n>
+Range<T*> range(T (&array)[n]) {
+  return Range<T*>(array, array + n);
+}
+
+typedef Range<const char*> StringPiece;
+typedef Range<char*> MutableStringPiece;
+typedef Range<const unsigned char*> ByteRange;
+typedef Range<unsigned char*> MutableByteRange;
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const StringPiece piece) {
+  os.write(piece.start(), piece.size());
+  return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const MutableStringPiece piece) {
+  os.write(piece.start(), piece.size());
+  return os;
+}
+
+/**
+ * Templated comparison operators
+ */
+
+template <class T>
+inline bool operator==(const Range<T>& lhs, const Range<T>& rhs) {
+  return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+template <class T>
+inline bool operator<(const Range<T>& lhs, const Range<T>& rhs) {
+  return lhs.compare(rhs) < 0;
+}
+
+/**
+ * Specializations of comparison operators for StringPiece
+ */
+
+namespace detail {
+
+template <class A, class B>
+struct ComparableAsStringPiece {
+  enum {
+    value =
+    (std::is_convertible<A, StringPiece>::value
+     && std::is_same<B, StringPiece>::value)
+    ||
+    (std::is_convertible<B, StringPiece>::value
+     && std::is_same<A, StringPiece>::value)
+  };
+};
+
+} // namespace detail
+
+/**
+ * operator== through conversion for Range<const char*>
+ */
+template <class T, class U>
+typename
+std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
+operator==(const T& lhs, const U& rhs) {
+  return StringPiece(lhs) == StringPiece(rhs);
+}
+
+/**
+ * operator< through conversion for Range<const char*>
+ */
+template <class T, class U>
+typename
+std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
+operator<(const T& lhs, const U& rhs) {
+  return StringPiece(lhs) < StringPiece(rhs);
+}
+
+/**
+ * operator> through conversion for Range<const char*>
+ */
+template <class T, class U>
+typename
+std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
+operator>(const T& lhs, const U& rhs) {
+  return StringPiece(lhs) > StringPiece(rhs);
+}
+
+/**
+ * operator< through conversion for Range<const char*>
+ */
+template <class T, class U>
+typename
+std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
+operator<=(const T& lhs, const U& rhs) {
+  return StringPiece(lhs) <= StringPiece(rhs);
+}
+
+/**
+ * operator> through conversion for Range<const char*>
+ */
+template <class T, class U>
+typename
+std::enable_if<detail::ComparableAsStringPiece<T, U>::value, bool>::type
+operator>=(const T& lhs, const U& rhs) {
+  return StringPiece(lhs) >= StringPiece(rhs);
+}
+
+// Do NOT use this, use SpookyHashV2 instead, see commment on hash() above.
+struct StringPieceHash {
+  std::size_t operator()(const StringPiece str) const {
+    return static_cast<std::size_t>(str.hash());
+  }
+};
+
+/**
+ * Finds substrings faster than brute force by borrowing from Boyer-Moore
+ */
+template <class T, class Comp>
+size_t qfind(const Range<T>& haystack,
+             const Range<T>& needle,
+             Comp eq) {
+  // Don't use std::search, use a Boyer-Moore-like trick by comparing
+  // the last characters first
+  auto const nsize = needle.size();
+  if (haystack.size() < nsize) {
+    return std::string::npos;
+  }
+  if (!nsize) return 0;
+  auto const nsize_1 = nsize - 1;
+  auto const lastNeedle = needle[nsize_1];
+
+  // Boyer-Moore skip value for the last char in the needle. Zero is
+  // not a valid value; skip will be computed the first time it's
+  // needed.
+  std::string::size_type skip = 0;
+
+  auto i = haystack.begin();
+  auto iEnd = haystack.end() - nsize_1;
+
+  while (i < iEnd) {
+    // Boyer-Moore: match the last element in the needle
+    while (!eq(i[nsize_1], lastNeedle)) {
+      if (++i == iEnd) {
+        // not found
+        return std::string::npos;
+      }
+    }
+    // Here we know that the last char matches
+    // Continue in pedestrian mode
+    for (size_t j = 0; ; ) {
+      assert(j < nsize);
+      if (!eq(i[j], needle[j])) {
+        // Not found, we can skip
+        // Compute the skip value lazily
+        if (skip == 0) {
+          skip = 1;
+          while (skip <= nsize_1 && !eq(needle[nsize_1 - skip], lastNeedle)) {
+            ++skip;
+          }
+        }
+        i += skip;
+        break;
+      }
+      // Check if done searching
+      if (++j == nsize) {
+        // Yay
+        return i - haystack.begin();
+      }
+    }
+  }
+  return std::string::npos;
+}
+
+namespace detail {
+
+size_t qfind_first_byte_of_nosse(const StringPiece haystack,
+                                 const StringPiece needles);
+
+#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
+size_t qfind_first_byte_of_sse42(const StringPiece haystack,
+                                 const StringPiece needles);
+
+inline size_t qfind_first_byte_of(const StringPiece haystack,
+                                  const StringPiece needles) {
+  static auto const qfind_first_byte_of_fn =
+	  qfind_first_byte_of_nosse;
+  return qfind_first_byte_of_fn(haystack, needles);
+}
+
+#else
+inline size_t qfind_first_byte_of(const StringPiece haystack,
+                                  const StringPiece needles) {
+  return qfind_first_byte_of_nosse(haystack, needles);
+}
+#endif // FOLLY_HAVE_EMMINTRIN_H
+
+} // namespace detail
+
+template <class T, class Comp>
+size_t qfind_first_of(const Range<T> & haystack,
+                      const Range<T> & needles,
+                      Comp eq) {
+  auto ret = std::find_first_of(haystack.begin(), haystack.end(),
+                                needles.begin(), needles.end(),
+                                eq);
+  return ret == haystack.end() ? std::string::npos : ret - haystack.begin();
+}
+
+struct AsciiCaseSensitive {
+  bool operator()(char lhs, char rhs) const {
+    return lhs == rhs;
+  }
+};
+
+/**
+ * Check if two ascii characters are case insensitive equal.
+ * The difference between the lower/upper case characters are the 6-th bit.
+ * We also check they are alpha chars, in case of xor = 32.
+ */
+struct AsciiCaseInsensitive {
+  bool operator()(char lhs, char rhs) const {
+    char k = lhs ^ rhs;
+    if (k == 0) return true;
+    if (k != 32) return false;
+    k = lhs | rhs;
+    return (k >= 'a' && k <= 'z');
+  }
+};
+
+extern const AsciiCaseSensitive asciiCaseSensitive;
+extern const AsciiCaseInsensitive asciiCaseInsensitive;
+
+template <class T>
+size_t qfind(const Range<T>& haystack,
+             const typename Range<T>::value_type& needle) {
+  auto pos = std::find(haystack.begin(), haystack.end(), needle);
+  return pos == haystack.end() ? std::string::npos : pos - haystack.data();
+}
+
+template <class T>
+size_t rfind(const Range<T>& haystack,
+             const typename Range<T>::value_type& needle) {
+  for (auto i = haystack.size(); i-- > 0; ) {
+    if (haystack[i] == needle) {
+      return i;
+    }
+  }
+  return std::string::npos;
+}
+
+// specialization for StringPiece
+template <>
+inline size_t qfind(const Range<const char*>& haystack, const char& needle) {
+  auto pos = static_cast<const char*>(
+    ::memchr(haystack.data(), needle, haystack.size()));
+  return pos == nullptr ? std::string::npos : pos - haystack.data();
+}
+
+#if FOLLY_HAVE_MEMRCHR
+template <>
+inline size_t rfind(const Range<const char*>& haystack, const char& needle) {
+  auto pos = static_cast<const char*>(
+    ::memrchr(haystack.data(), needle, haystack.size()));
+  return pos == nullptr ? std::string::npos : pos - haystack.data();
+}
+#endif
+
+// specialization for ByteRange
+template <>
+inline size_t qfind(const Range<const unsigned char*>& haystack,
+                    const unsigned char& needle) {
+  auto pos = static_cast<const unsigned char*>(
+    ::memchr(haystack.data(), needle, haystack.size()));
+  return pos == nullptr ? std::string::npos : pos - haystack.data();
+}
+
+#if FOLLY_HAVE_MEMRCHR
+template <>
+inline size_t rfind(const Range<const unsigned char*>& haystack,
+                    const unsigned char& needle) {
+  auto pos = static_cast<const unsigned char*>(
+    ::memrchr(haystack.data(), needle, haystack.size()));
+  return pos == nullptr ? std::string::npos : pos - haystack.data();
+}
+#endif
+
+template <class T>
+size_t qfind_first_of(const Range<T>& haystack,
+                      const Range<T>& needles) {
+  return qfind_first_of(haystack, needles, asciiCaseSensitive);
+}
+
+// specialization for StringPiece
+template <>
+inline size_t qfind_first_of(const Range<const char*>& haystack,
+                             const Range<const char*>& needles) {
+  return detail::qfind_first_byte_of(haystack, needles);
+}
+
+// specialization for ByteRange
+template <>
+inline size_t qfind_first_of(const Range<const unsigned char*>& haystack,
+                             const Range<const unsigned char*>& needles) {
+  return detail::qfind_first_byte_of(StringPiece(haystack),
+                                     StringPiece(needles));
+}
+
+template<class Key, class Enable>
+struct hasher;
+
+template <class T>
+struct hasher<folly::Range<T*>,
+              typename std::enable_if<std::is_pod<T>::value, void>::type> {
+  size_t operator()(folly::Range<T*> r) const {
+    return hash::SpookyHashV2::Hash64(r.begin(), r.size() * sizeof(T), 0);
+  }
+};
+
+}  // !namespace folly
+
+#pragma GCC diagnostic pop
+
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(folly::Range);
+
+#endif // FOLLY_RANGE_H_
diff --git a/faux-folly/folly/ScopeGuard.h b/faux-folly/folly/ScopeGuard.h
new file mode 100644
index 0000000..64ddc60
--- /dev/null
+++ b/faux-folly/folly/ScopeGuard.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_SCOPEGUARD_H_
+#define FOLLY_SCOPEGUARD_H_
+
+#include <cstddef>
+#include <functional>
+#include <new>
+
+#include <folly/Preprocessor.h>
+#include <folly/detail/UncaughtExceptionCounter.h>
+
+namespace folly {
+
+/**
+ * ScopeGuard is a general implementation of the "Initialization is
+ * Resource Acquisition" idiom.  Basically, it guarantees that a function
+ * is executed upon leaving the currrent scope unless otherwise told.
+ *
+ * The makeGuard() function is used to create a new ScopeGuard object.
+ * It can be instantiated with a lambda function, a std::function<void()>,
+ * a functor, or a void(*)() function pointer.
+ *
+ *
+ * Usage example: Add a friend to memory iff it is also added to the db.
+ *
+ * void User::addFriend(User& newFriend) {
+ *   // add the friend to memory
+ *   friends_.push_back(&newFriend);
+ *
+ *   // If the db insertion that follows fails, we should
+ *   // remove it from memory.
+ *   // (You could also declare this as "auto guard = makeGuard(...)")
+ *   ScopeGuard guard = makeGuard([&] { friends_.pop_back(); });
+ *
+ *   // this will throw an exception upon error, which
+ *   // makes the ScopeGuard execute UserCont::pop_back()
+ *   // once the Guard's destructor is called.
+ *   db_->addFriend(GetName(), newFriend.GetName());
+ *
+ *   // an exception was not thrown, so don't execute
+ *   // the Guard.
+ *   guard.dismiss();
+ * }
+ *
+ * Examine ScopeGuardTest.cpp for some more sample usage.
+ *
+ * Stolen from:
+ *   Andrei's and Petru Marginean's CUJ article:
+ *     http://drdobbs.com/184403758
+ *   and the loki library:
+ *     http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer
+ *   and triendl.kj article:
+ *     http://www.codeproject.com/KB/cpp/scope_guard.aspx
+ */
+class ScopeGuardImplBase {
+ public:
+  void dismiss() noexcept {
+    dismissed_ = true;
+  }
+
+ protected:
+  ScopeGuardImplBase()
+    : dismissed_(false) {}
+
+  ScopeGuardImplBase(ScopeGuardImplBase&& other) noexcept
+    : dismissed_(other.dismissed_) {
+    other.dismissed_ = true;
+  }
+
+  bool dismissed_;
+};
+
+template <typename FunctionType>
+class ScopeGuardImpl : public ScopeGuardImplBase {
+ public:
+  explicit ScopeGuardImpl(const FunctionType& fn)
+    : function_(fn) {}
+
+  explicit ScopeGuardImpl(FunctionType&& fn)
+    : function_(std::move(fn)) {}
+
+  ScopeGuardImpl(ScopeGuardImpl&& other)
+    : ScopeGuardImplBase(std::move(other))
+    , function_(std::move(other.function_)) {
+  }
+
+  ~ScopeGuardImpl() noexcept {
+    if (!dismissed_) {
+      execute();
+    }
+  }
+
+ private:
+  void* operator new(size_t) = delete;
+
+  void execute() noexcept { function_(); }
+
+  FunctionType function_;
+};
+
+template <typename FunctionType>
+ScopeGuardImpl<typename std::decay<FunctionType>::type>
+makeGuard(FunctionType&& fn) {
+  return ScopeGuardImpl<typename std::decay<FunctionType>::type>(
+      std::forward<FunctionType>(fn));
+}
+
+/**
+ * This is largely unneeded if you just use auto for your guards.
+ */
+typedef ScopeGuardImplBase&& ScopeGuard;
+
+namespace detail {
+
+#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
+    defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
+
+/**
+ * ScopeGuard used for executing a function when leaving the current scope
+ * depending on the presence of a new uncaught exception.
+ *
+ * If the executeOnException template parameter is true, the function is
+ * executed if a new uncaught exception is present at the end of the scope.
+ * If the parameter is false, then the function is executed if no new uncaught
+ * exceptions are present at the end of the scope.
+ *
+ * Used to implement SCOPE_FAIL and SCOPE_SUCCES below.
+ */
+template <typename FunctionType, bool executeOnException>
+class ScopeGuardForNewException {
+ public:
+  explicit ScopeGuardForNewException(const FunctionType& fn)
+      : function_(fn) {
+  }
+
+  explicit ScopeGuardForNewException(FunctionType&& fn)
+      : function_(std::move(fn)) {
+  }
+
+  ScopeGuardForNewException(ScopeGuardForNewException&& other)
+      : function_(std::move(other.function_))
+      , exceptionCounter_(std::move(other.exceptionCounter_)) {
+  }
+
+  ~ScopeGuardForNewException() noexcept(executeOnException) {
+    if (executeOnException == exceptionCounter_.isNewUncaughtException()) {
+      function_();
+    }
+  }
+
+ private:
+  ScopeGuardForNewException(const ScopeGuardForNewException& other) = delete;
+
+  void* operator new(size_t) = delete;
+
+  FunctionType function_;
+  UncaughtExceptionCounter exceptionCounter_;
+};
+
+/**
+ * Internal use for the macro SCOPE_FAIL below
+ */
+enum class ScopeGuardOnFail {};
+
+template <typename FunctionType>
+ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>
+operator+(detail::ScopeGuardOnFail, FunctionType&& fn) {
+  return
+      ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>(
+      std::forward<FunctionType>(fn));
+}
+
+/**
+ * Internal use for the macro SCOPE_SUCCESS below
+ */
+enum class ScopeGuardOnSuccess {};
+
+template <typename FunctionType>
+ScopeGuardForNewException<typename std::decay<FunctionType>::type, false>
+operator+(ScopeGuardOnSuccess, FunctionType&& fn) {
+  return
+      ScopeGuardForNewException<typename std::decay<FunctionType>::type, false>(
+      std::forward<FunctionType>(fn));
+}
+
+#endif // native uncaught_exception() supported
+
+/**
+ * Internal use for the macro SCOPE_EXIT below
+ */
+enum class ScopeGuardOnExit {};
+
+template <typename FunctionType>
+ScopeGuardImpl<typename std::decay<FunctionType>::type>
+operator+(detail::ScopeGuardOnExit, FunctionType&& fn) {
+  return ScopeGuardImpl<typename std::decay<FunctionType>::type>(
+      std::forward<FunctionType>(fn));
+}
+} // namespace detail
+
+} // folly
+
+#define SCOPE_EXIT \
+  auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
+  = ::folly::detail::ScopeGuardOnExit() + [&]() noexcept
+
+#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
+    defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
+#define SCOPE_FAIL \
+  auto FB_ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) \
+  = ::folly::detail::ScopeGuardOnFail() + [&]() noexcept
+
+#define SCOPE_SUCCESS \
+  auto FB_ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) \
+  = ::folly::detail::ScopeGuardOnSuccess() + [&]()
+#endif // native uncaught_exception() supported
+
+#endif // FOLLY_SCOPEGUARD_H_
diff --git a/faux-folly/folly/SpookyHashV1.cpp b/faux-folly/folly/SpookyHashV1.cpp
new file mode 100644
index 0000000..2aa9de7
--- /dev/null
+++ b/faux-folly/folly/SpookyHashV1.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// Spooky Hash
+// A 128-bit noncryptographic hash, for checksums and table lookup
+// By Bob Jenkins.  Public domain.
+//   Oct 31 2010: published framework, disclaimer ShortHash isn't right
+//   Nov 7 2010: disabled ShortHash
+//   Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
+//   April 10 2012: buffer overflow on platforms without unaligned reads
+//   July 12 2012: was passing out variables in final to in/out in short
+//   July 30 2012: I reintroduced the buffer overflow
+
+#include <folly/SpookyHashV1.h>
+
+#include <cstring>
+
+#define ALLOW_UNALIGNED_READS 1
+
+namespace folly {
+namespace hash {
+
+//
+// short hash ... it could be used on any message,
+// but it's used by Spooky just for short messages.
+//
+void SpookyHashV1::Short(
+    const void *message,
+    size_t length,
+    uint64_t *hash1,
+    uint64_t *hash2)
+{
+    uint64_t buf[2*sc_numVars];
+    union
+    {
+        const uint8_t *p8;
+        uint32_t *p32;
+        uint64_t *p64;
+        size_t i;
+    } u;
+
+    u.p8 = (const uint8_t *)message;
+
+    if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
+    {
+        memcpy(buf, message, length);
+        u.p64 = buf;
+    }
+
+    size_t remainder = length%32;
+    uint64_t a=*hash1;
+    uint64_t b=*hash2;
+    uint64_t c=sc_const;
+    uint64_t d=sc_const;
+
+    if (length > 15)
+    {
+        const uint64_t *end = u.p64 + (length/32)*4;
+
+        // handle all complete sets of 32 bytes
+        for (; u.p64 < end; u.p64 += 4)
+        {
+            c += u.p64[0];
+            d += u.p64[1];
+            ShortMix(a,b,c,d);
+            a += u.p64[2];
+            b += u.p64[3];
+        }
+
+        //Handle the case of 16+ remaining bytes.
+        if (remainder >= 16)
+        {
+            c += u.p64[0];
+            d += u.p64[1];
+            ShortMix(a,b,c,d);
+            u.p64 += 2;
+            remainder -= 16;
+        }
+    }
+
+    // Handle the last 0..15 bytes, and its length
+    d = ((uint64_t)length) << 56;
+    switch (remainder)
+    {
+    case 15:
+    d += ((uint64_t)u.p8[14]) << 48;
+    case 14:
+        d += ((uint64_t)u.p8[13]) << 40;
+    case 13:
+        d += ((uint64_t)u.p8[12]) << 32;
+    case 12:
+        d += u.p32[2];
+        c += u.p64[0];
+        break;
+    case 11:
+        d += ((uint64_t)u.p8[10]) << 16;
+    case 10:
+        d += ((uint64_t)u.p8[9]) << 8;
+    case 9:
+        d += (uint64_t)u.p8[8];
+    case 8:
+        c += u.p64[0];
+        break;
+    case 7:
+        c += ((uint64_t)u.p8[6]) << 48;
+    case 6:
+        c += ((uint64_t)u.p8[5]) << 40;
+    case 5:
+        c += ((uint64_t)u.p8[4]) << 32;
+    case 4:
+        c += u.p32[0];
+        break;
+    case 3:
+        c += ((uint64_t)u.p8[2]) << 16;
+    case 2:
+        c += ((uint64_t)u.p8[1]) << 8;
+    case 1:
+        c += (uint64_t)u.p8[0];
+        break;
+    case 0:
+        c += sc_const;
+        d += sc_const;
+    }
+    ShortEnd(a,b,c,d);
+    *hash1 = a;
+    *hash2 = b;
+}
+
+
+
+
+// do the whole hash in one call
+void SpookyHashV1::Hash128(
+    const void *message,
+    size_t length,
+    uint64_t *hash1,
+    uint64_t *hash2)
+{
+    if (length < sc_bufSize)
+    {
+        Short(message, length, hash1, hash2);
+        return;
+    }
+
+    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+    uint64_t buf[sc_numVars];
+    uint64_t *end;
+    union
+    {
+        const uint8_t *p8;
+        uint64_t *p64;
+        size_t i;
+    } u;
+    size_t remainder;
+
+    h0=h3=h6=h9  = *hash1;
+    h1=h4=h7=h10 = *hash2;
+    h2=h5=h8=h11 = sc_const;
+
+    u.p8 = (const uint8_t *)message;
+    end = u.p64 + (length/sc_blockSize)*sc_numVars;
+
+    // handle all whole sc_blockSize blocks of bytes
+    if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
+    {
+        while (u.p64 < end)
+        {
+            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+    else
+    {
+        while (u.p64 < end)
+        {
+            memcpy(buf, u.p64, sc_blockSize);
+            Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+
+    // handle the last partial block of sc_blockSize bytes
+    remainder = (length - ((const uint8_t *)end-(const uint8_t *)message));
+    memcpy(buf, end, remainder);
+    memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder);
+    ((uint8_t *)buf)[sc_blockSize-1] = remainder;
+    Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+
+    // do some final mixing
+    End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+    *hash1 = h0;
+    *hash2 = h1;
+}
+
+
+
+// init spooky state
+void SpookyHashV1::Init(uint64_t seed1, uint64_t seed2)
+{
+    m_length = 0;
+    m_remainder = 0;
+    m_state[0] = seed1;
+    m_state[1] = seed2;
+}
+
+
+// add a message fragment to the state
+void SpookyHashV1::Update(const void *message, size_t length)
+{
+    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+    size_t newLength = length + m_remainder;
+    uint8_t  remainder;
+    union
+    {
+        const uint8_t *p8;
+        uint64_t *p64;
+        size_t i;
+    } u;
+    const uint64_t *end;
+
+    // Is this message fragment too short?  If it is, stuff it away.
+    if (newLength < sc_bufSize)
+    {
+        memcpy(&((uint8_t *)m_data)[m_remainder], message, length);
+        m_length = length + m_length;
+        m_remainder = (uint8_t)newLength;
+        return;
+    }
+
+    // init the variables
+    if (m_length < sc_bufSize)
+    {
+        h0=h3=h6=h9  = m_state[0];
+        h1=h4=h7=h10 = m_state[1];
+        h2=h5=h8=h11 = sc_const;
+    }
+    else
+    {
+        h0 = m_state[0];
+        h1 = m_state[1];
+        h2 = m_state[2];
+        h3 = m_state[3];
+        h4 = m_state[4];
+        h5 = m_state[5];
+        h6 = m_state[6];
+        h7 = m_state[7];
+        h8 = m_state[8];
+        h9 = m_state[9];
+        h10 = m_state[10];
+        h11 = m_state[11];
+    }
+    m_length = length + m_length;
+
+    // if we've got anything stuffed away, use it now
+    if (m_remainder)
+    {
+        uint8_t prefix = sc_bufSize-m_remainder;
+        memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix);
+        u.p64 = m_data;
+        Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        u.p8 = ((const uint8_t *)message) + prefix;
+        length -= prefix;
+    }
+    else
+    {
+        u.p8 = (const uint8_t *)message;
+    }
+
+    // handle all whole blocks of sc_blockSize bytes
+    end = u.p64 + (length/sc_blockSize)*sc_numVars;
+    remainder = (uint8_t)(length-((const uint8_t *)end-u.p8));
+    if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
+    {
+        while (u.p64 < end)
+        {
+            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+    else
+    {
+        while (u.p64 < end)
+        {
+            memcpy(m_data, u.p8, sc_blockSize);
+            Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+
+    // stuff away the last few bytes
+    m_remainder = remainder;
+    memcpy(m_data, end, remainder);
+
+    // stuff away the variables
+    m_state[0] = h0;
+    m_state[1] = h1;
+    m_state[2] = h2;
+    m_state[3] = h3;
+    m_state[4] = h4;
+    m_state[5] = h5;
+    m_state[6] = h6;
+    m_state[7] = h7;
+    m_state[8] = h8;
+    m_state[9] = h9;
+    m_state[10] = h10;
+    m_state[11] = h11;
+}
+
+
+// report the hash for the concatenation of all message fragments so far
+void SpookyHashV1::Final(uint64_t *hash1, uint64_t *hash2)
+{
+    // init the variables
+    if (m_length < sc_bufSize)
+    {
+        *hash1 = m_state[0];
+        *hash2 = m_state[1];
+        Short( m_data, m_length, hash1, hash2);
+        return;
+    }
+
+    const uint64_t *data = (const uint64_t *)m_data;
+    uint8_t remainder = m_remainder;
+
+    uint64_t h0 = m_state[0];
+    uint64_t h1 = m_state[1];
+    uint64_t h2 = m_state[2];
+    uint64_t h3 = m_state[3];
+    uint64_t h4 = m_state[4];
+    uint64_t h5 = m_state[5];
+    uint64_t h6 = m_state[6];
+    uint64_t h7 = m_state[7];
+    uint64_t h8 = m_state[8];
+    uint64_t h9 = m_state[9];
+    uint64_t h10 = m_state[10];
+    uint64_t h11 = m_state[11];
+
+    if (remainder >= sc_blockSize)
+    {
+        // m_data can contain two blocks; handle any whole first block
+        Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        data += sc_numVars;
+        remainder -= sc_blockSize;
+    }
+
+    // mix in the last partial block, and the length mod sc_blockSize
+    memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder));
+
+    ((uint8_t *)data)[sc_blockSize-1] = remainder;
+    Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+
+    // do some final mixing
+    End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+
+    *hash1 = h0;
+    *hash2 = h1;
+}
+
+}  // namespace hash
+}  // namespace folly
diff --git a/faux-folly/folly/SpookyHashV1.h b/faux-folly/folly/SpookyHashV1.h
new file mode 100644
index 0000000..789b48a
--- /dev/null
+++ b/faux-folly/folly/SpookyHashV1.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// This is version 1 of SpookyHash, incompatible with version 2.
+//
+// SpookyHash: a 128-bit noncryptographic hash function
+// By Bob Jenkins, public domain
+//   Oct 31 2010: alpha, framework + SpookyHash::Mix appears right
+//   Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right
+//   Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas
+//   Feb  2 2012: production, same bits as beta
+//   Feb  5 2012: adjusted definitions of uint* to be more portable
+//   Mar 30 2012: 3 bytes/cycle, not 4.  Alpha was 4 but wasn't thorough enough.
+//
+// Up to 3 bytes/cycle for long messages.  Reasonably fast for short messages.
+// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.
+//
+// This was developed for and tested on 64-bit x86-compatible processors.
+// It assumes the processor is little-endian.  There is a macro
+// controlling whether unaligned reads are allowed (by default they are).
+// This should be an equally good hash on big-endian machines, but it will
+// compute different results on them than on little-endian machines.
+//
+// Google's CityHash has similar specs to SpookyHash, and CityHash is faster
+// on some platforms.  MD4 and MD5 also have similar specs, but they are orders
+// of magnitude slower.  CRCs are two or more times slower, but unlike
+// SpookyHash, they have nice math for combining the CRCs of pieces to form
+// the CRCs of wholes.  There are also cryptographic hashes, but those are even
+// slower than MD5.
+//
+
+#ifndef FOLLY_SPOOKYHASHV1_H_
+#define FOLLY_SPOOKYHASHV1_H_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace folly {
+namespace hash {
+
+class SpookyHashV1
+{
+public:
+    //
+    // SpookyHash: hash a single message in one call, produce 128-bit output
+    //
+    static void Hash128(
+        const void *message,  // message to hash
+        size_t length,        // length of message in bytes
+        uint64_t *hash1,      // in/out: in seed 1, out hash value 1
+        uint64_t *hash2);     // in/out: in seed 2, out hash value 2
+
+    //
+    // Hash64: hash a single message in one call, return 64-bit output
+    //
+    static uint64_t Hash64(
+        const void *message,  // message to hash
+        size_t length,        // length of message in bytes
+        uint64_t seed)        // seed
+    {
+        uint64_t hash1 = seed;
+        Hash128(message, length, &hash1, &seed);
+        return hash1;
+    }
+
+    //
+    // Hash32: hash a single message in one call, produce 32-bit output
+    //
+    static uint32_t Hash32(
+        const void *message,  // message to hash
+        size_t length,        // length of message in bytes
+        uint32_t seed)        // seed
+    {
+        uint64_t hash1 = seed, hash2 = seed;
+        Hash128(message, length, &hash1, &hash2);
+        return (uint32_t)hash1;
+    }
+
+    //
+    // Init: initialize the context of a SpookyHash
+    //
+    void Init(
+        uint64_t seed1,     // any 64-bit value will do, including 0
+        uint64_t seed2);    // different seeds produce independent hashes
+
+    //
+    // Update: add a piece of a message to a SpookyHash state
+    //
+    void Update(
+        const void *message,  // message fragment
+        size_t length);       // length of message fragment in bytes
+
+
+    //
+    // Final: compute the hash for the current SpookyHash state
+    //
+    // This does not modify the state; you can keep updating it afterward
+    //
+    // The result is the same as if SpookyHash() had been called with
+    // all the pieces concatenated into one message.
+    //
+    void Final(
+        uint64_t *hash1,  // out only: first 64 bits of hash value.
+        uint64_t *hash2); // out only: second 64 bits of hash value.
+
+    //
+    // left rotate a 64-bit value by k bytes
+    //
+    static inline uint64_t Rot64(uint64_t x, int k)
+    {
+        return (x << k) | (x >> (64 - k));
+    }
+
+    //
+    // This is used if the input is 96 bytes long or longer.
+    //
+    // The internal state is fully overwritten every 96 bytes.
+    // Every input bit appears to cause at least 128 bits of entropy
+    // before 96 other bytes are combined, when run forward or backward
+    //   For every input bit,
+    //   Two inputs differing in just that input bit
+    //   Where "differ" means xor or subtraction
+    //   And the base value is random
+    //   When run forward or backwards one Mix
+    // I tried 3 pairs of each; they all differed by at least 212 bits.
+    //
+    static inline void Mix(
+        const uint64_t *data,
+        uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3,
+        uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7,
+        uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11)
+    {
+      s0 += data[0];   s2 ^= s10; s11 ^= s0;  s0 = Rot64(s0,11);   s11 += s1;
+      s1 += data[1];   s3 ^= s11; s0 ^= s1;   s1 = Rot64(s1,32);   s0 += s2;
+      s2 += data[2];   s4 ^= s0;  s1 ^= s2;   s2 = Rot64(s2,43);   s1 += s3;
+      s3 += data[3];   s5 ^= s1;  s2 ^= s3;   s3 = Rot64(s3,31);   s2 += s4;
+      s4 += data[4];   s6 ^= s2;  s3 ^= s4;   s4 = Rot64(s4,17);   s3 += s5;
+      s5 += data[5];   s7 ^= s3;  s4 ^= s5;   s5 = Rot64(s5,28);   s4 += s6;
+      s6 += data[6];   s8 ^= s4;  s5 ^= s6;   s6 = Rot64(s6,39);   s5 += s7;
+      s7 += data[7];   s9 ^= s5;  s6 ^= s7;   s7 = Rot64(s7,57);   s6 += s8;
+      s8 += data[8];   s10 ^= s6; s7 ^= s8;   s8 = Rot64(s8,55);   s7 += s9;
+      s9 += data[9];   s11 ^= s7; s8 ^= s9;   s9 = Rot64(s9,54);   s8 += s10;
+      s10 += data[10]; s0 ^= s8;  s9 ^= s10;  s10 = Rot64(s10,22); s9 += s11;
+      s11 += data[11]; s1 ^= s9;  s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;
+    }
+
+    //
+    // Mix all 12 inputs together so that h0, h1 are a hash of them all.
+    //
+    // For two inputs differing in just the input bits
+    // Where "differ" means xor or subtraction
+    // And the base value is random, or a counting value starting at that bit
+    // The final result will have each bit of h0, h1 flip
+    // For every input bit,
+    // with probability 50 +- .3%
+    // For every pair of input bits,
+    // with probability 50 +- 3%
+    //
+    // This does not rely on the last Mix() call having already mixed some.
+    // Two iterations was almost good enough for a 64-bit result, but a
+    // 128-bit result is reported, so End() does three iterations.
+    //
+    static inline void EndPartial(
+        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,
+        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,
+        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)
+    {
+        h11+= h1;    h2 ^= h11;   h1 = Rot64(h1,44);
+        h0 += h2;    h3 ^= h0;    h2 = Rot64(h2,15);
+        h1 += h3;    h4 ^= h1;    h3 = Rot64(h3,34);
+        h2 += h4;    h5 ^= h2;    h4 = Rot64(h4,21);
+        h3 += h5;    h6 ^= h3;    h5 = Rot64(h5,38);
+        h4 += h6;    h7 ^= h4;    h6 = Rot64(h6,33);
+        h5 += h7;    h8 ^= h5;    h7 = Rot64(h7,10);
+        h6 += h8;    h9 ^= h6;    h8 = Rot64(h8,13);
+        h7 += h9;    h10^= h7;    h9 = Rot64(h9,38);
+        h8 += h10;   h11^= h8;    h10= Rot64(h10,53);
+        h9 += h11;   h0 ^= h9;    h11= Rot64(h11,42);
+        h10+= h0;    h1 ^= h10;   h0 = Rot64(h0,54);
+    }
+
+    static inline void End(
+        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,
+        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,
+        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)
+    {
+        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+    }
+
+    //
+    // The goal is for each bit of the input to expand into 128 bits of
+    //   apparent entropy before it is fully overwritten.
+    // n trials both set and cleared at least m bits of h0 h1 h2 h3
+    //   n: 2   m: 29
+    //   n: 3   m: 46
+    //   n: 4   m: 57
+    //   n: 5   m: 107
+    //   n: 6   m: 146
+    //   n: 7   m: 152
+    // when run forwards or backwards
+    // for all 1-bit and 2-bit diffs
+    // with diffs defined by either xor or subtraction
+    // with a base of all zeros plus a counter, or plus another bit, or random
+    //
+    static inline void ShortMix(uint64_t &h0, uint64_t &h1,
+                                uint64_t &h2, uint64_t &h3)
+    {
+        h2 = Rot64(h2,50);  h2 += h3;  h0 ^= h2;
+        h3 = Rot64(h3,52);  h3 += h0;  h1 ^= h3;
+        h0 = Rot64(h0,30);  h0 += h1;  h2 ^= h0;
+        h1 = Rot64(h1,41);  h1 += h2;  h3 ^= h1;
+        h2 = Rot64(h2,54);  h2 += h3;  h0 ^= h2;
+        h3 = Rot64(h3,48);  h3 += h0;  h1 ^= h3;
+        h0 = Rot64(h0,38);  h0 += h1;  h2 ^= h0;
+        h1 = Rot64(h1,37);  h1 += h2;  h3 ^= h1;
+        h2 = Rot64(h2,62);  h2 += h3;  h0 ^= h2;
+        h3 = Rot64(h3,34);  h3 += h0;  h1 ^= h3;
+        h0 = Rot64(h0,5);   h0 += h1;  h2 ^= h0;
+        h1 = Rot64(h1,36);  h1 += h2;  h3 ^= h1;
+    }
+
+    //
+    // Mix all 4 inputs together so that h0, h1 are a hash of them all.
+    //
+    // For two inputs differing in just the input bits
+    // Where "differ" means xor or subtraction
+    // And the base value is random, or a counting value starting at that bit
+    // The final result will have each bit of h0, h1 flip
+    // For every input bit,
+    // with probability 50 +- .3% (it is probably better than that)
+    // For every pair of input bits,
+    // with probability 50 +- .75% (the worst case is approximately that)
+    //
+    static inline void ShortEnd(uint64_t &h0, uint64_t &h1,
+                                uint64_t &h2, uint64_t &h3)
+    {
+        h3 ^= h2;  h2 = Rot64(h2,15);  h3 += h2;
+        h0 ^= h3;  h3 = Rot64(h3,52);  h0 += h3;
+        h1 ^= h0;  h0 = Rot64(h0,26);  h1 += h0;
+        h2 ^= h1;  h1 = Rot64(h1,51);  h2 += h1;
+        h3 ^= h2;  h2 = Rot64(h2,28);  h3 += h2;
+        h0 ^= h3;  h3 = Rot64(h3,9);   h0 += h3;
+        h1 ^= h0;  h0 = Rot64(h0,47);  h1 += h0;
+        h2 ^= h1;  h1 = Rot64(h1,54);  h2 += h1;
+        h3 ^= h2;  h2 = Rot64(h2,32);  h3 += h2;
+        h0 ^= h3;  h3 = Rot64(h3,25);  h0 += h3;
+        h1 ^= h0;  h0 = Rot64(h0,63);  h1 += h0;
+    }
+
+private:
+
+    //
+    // Short is used for messages under 192 bytes in length
+    // Short has a low startup cost, the normal mode is good for long
+    // keys, the cost crossover is at about 192 bytes.  The two modes were
+    // held to the same quality bar.
+    //
+    static void Short(
+        const void *message,  // message (byte array, not necessarily aligned)
+        size_t length,        // length of message (in bytes)
+        uint64_t *hash1,      // in/out: in the seed, out the hash value
+        uint64_t *hash2);     // in/out: in the seed, out the hash value
+
+    // number of uint64_t's in internal state
+    static const size_t sc_numVars = 12;
+
+    // size of the internal state
+    static const size_t sc_blockSize = sc_numVars*8;
+
+    // size of buffer of unhashed data, in bytes
+    static const size_t sc_bufSize = 2*sc_blockSize;
+
+    //
+    // sc_const: a constant which:
+    //  * is not zero
+    //  * is odd
+    //  * is a not-very-regular mix of 1's and 0's
+    //  * does not need any other special mathematical properties
+    //
+    static const uint64_t sc_const = 0xdeadbeefdeadbeefLL;
+
+    uint64_t m_data[2*sc_numVars];  // unhashed data, for partial messages
+    uint64_t m_state[sc_numVars];   // internal state of the hash
+    size_t m_length;                // total length of the input so far
+    uint8_t  m_remainder;           // length of unhashed data stashed in m_data
+};
+
+}  // namespace hash
+}  // namespace folly
+
+#endif
diff --git a/faux-folly/folly/SpookyHashV2.cpp b/faux-folly/folly/SpookyHashV2.cpp
new file mode 100644
index 0000000..45f640a
--- /dev/null
+++ b/faux-folly/folly/SpookyHashV2.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// Spooky Hash
+// A 128-bit noncryptographic hash, for checksums and table lookup
+// By Bob Jenkins.  Public domain.
+//   Oct 31 2010: published framework, disclaimer ShortHash isn't right
+//   Nov 7 2010: disabled ShortHash
+//   Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
+//   April 10 2012: buffer overflow on platforms without unaligned reads
+//   July 12 2012: was passing out variables in final to in/out in short
+//   July 30 2012: I reintroduced the buffer overflow
+//   August 5 2012: SpookyV2: d = should be d += in short hash, and remove
+//                  extra mix from long hash
+
+#include <folly/SpookyHashV2.h>
+
+#include <cstring>
+
+#define ALLOW_UNALIGNED_READS 1
+
+namespace folly {
+namespace hash {
+
+//
+// short hash ... it could be used on any message,
+// but it's used by Spooky just for short messages.
+//
+void SpookyHashV2::Short(
+    const void *message,
+    size_t length,
+    uint64_t *hash1,
+    uint64_t *hash2)
+{
+    uint64_t buf[2*sc_numVars];
+    union
+    {
+        const uint8_t *p8;
+        uint32_t *p32;
+        uint64_t *p64;
+        size_t i;
+    } u;
+
+    u.p8 = (const uint8_t *)message;
+
+    if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
+    {
+        memcpy(buf, message, length);
+        u.p64 = buf;
+    }
+
+    size_t remainder = length%32;
+    uint64_t a=*hash1;
+    uint64_t b=*hash2;
+    uint64_t c=sc_const;
+    uint64_t d=sc_const;
+
+    if (length > 15)
+    {
+        const uint64_t *end = u.p64 + (length/32)*4;
+
+        // handle all complete sets of 32 bytes
+        for (; u.p64 < end; u.p64 += 4)
+        {
+            c += u.p64[0];
+            d += u.p64[1];
+            ShortMix(a,b,c,d);
+            a += u.p64[2];
+            b += u.p64[3];
+        }
+
+        //Handle the case of 16+ remaining bytes.
+        if (remainder >= 16)
+        {
+            c += u.p64[0];
+            d += u.p64[1];
+            ShortMix(a,b,c,d);
+            u.p64 += 2;
+            remainder -= 16;
+        }
+    }
+
+    // Handle the last 0..15 bytes, and its length
+    d += ((uint64_t)length) << 56;
+    switch (remainder)
+    {
+    case 15:
+    d += ((uint64_t)u.p8[14]) << 48;
+    case 14:
+        d += ((uint64_t)u.p8[13]) << 40;
+    case 13:
+        d += ((uint64_t)u.p8[12]) << 32;
+    case 12:
+        d += u.p32[2];
+        c += u.p64[0];
+        break;
+    case 11:
+        d += ((uint64_t)u.p8[10]) << 16;
+    case 10:
+        d += ((uint64_t)u.p8[9]) << 8;
+    case 9:
+        d += (uint64_t)u.p8[8];
+    case 8:
+        c += u.p64[0];
+        break;
+    case 7:
+        c += ((uint64_t)u.p8[6]) << 48;
+    case 6:
+        c += ((uint64_t)u.p8[5]) << 40;
+    case 5:
+        c += ((uint64_t)u.p8[4]) << 32;
+    case 4:
+        c += u.p32[0];
+        break;
+    case 3:
+        c += ((uint64_t)u.p8[2]) << 16;
+    case 2:
+        c += ((uint64_t)u.p8[1]) << 8;
+    case 1:
+        c += (uint64_t)u.p8[0];
+        break;
+    case 0:
+        c += sc_const;
+        d += sc_const;
+    }
+    ShortEnd(a,b,c,d);
+    *hash1 = a;
+    *hash2 = b;
+}
+
+
+
+
+// do the whole hash in one call
+void SpookyHashV2::Hash128(
+    const void *message,
+    size_t length,
+    uint64_t *hash1,
+    uint64_t *hash2)
+{
+    if (length < sc_bufSize)
+    {
+        Short(message, length, hash1, hash2);
+        return;
+    }
+
+    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+    uint64_t buf[sc_numVars];
+    uint64_t *end;
+    union
+    {
+        const uint8_t *p8;
+        uint64_t *p64;
+        size_t i;
+    } u;
+    size_t remainder;
+
+    h0=h3=h6=h9  = *hash1;
+    h1=h4=h7=h10 = *hash2;
+    h2=h5=h8=h11 = sc_const;
+
+    u.p8 = (const uint8_t *)message;
+    end = u.p64 + (length/sc_blockSize)*sc_numVars;
+
+    // handle all whole sc_blockSize blocks of bytes
+    if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
+    {
+        while (u.p64 < end)
+        {
+            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+    else
+    {
+        while (u.p64 < end)
+        {
+            memcpy(buf, u.p64, sc_blockSize);
+            Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+
+    // handle the last partial block of sc_blockSize bytes
+    remainder = (length - ((const uint8_t *)end-(const uint8_t *)message));
+    memcpy(buf, end, remainder);
+    memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder);
+    ((uint8_t *)buf)[sc_blockSize-1] = remainder;
+
+    // do some final mixing
+    End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+    *hash1 = h0;
+    *hash2 = h1;
+}
+
+
+
+// init spooky state
+void SpookyHashV2::Init(uint64_t seed1, uint64_t seed2)
+{
+    m_length = 0;
+    m_remainder = 0;
+    m_state[0] = seed1;
+    m_state[1] = seed2;
+}
+
+
+// add a message fragment to the state
+void SpookyHashV2::Update(const void *message, size_t length)
+{
+    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+    size_t newLength = length + m_remainder;
+    uint8_t  remainder;
+    union
+    {
+        const uint8_t *p8;
+        uint64_t *p64;
+        size_t i;
+    } u;
+    const uint64_t *end;
+
+    // Is this message fragment too short?  If it is, stuff it away.
+    if (newLength < sc_bufSize)
+    {
+        memcpy(&((uint8_t *)m_data)[m_remainder], message, length);
+        m_length = length + m_length;
+        m_remainder = (uint8_t)newLength;
+        return;
+    }
+
+    // init the variables
+    if (m_length < sc_bufSize)
+    {
+        h0=h3=h6=h9  = m_state[0];
+        h1=h4=h7=h10 = m_state[1];
+        h2=h5=h8=h11 = sc_const;
+    }
+    else
+    {
+        h0 = m_state[0];
+        h1 = m_state[1];
+        h2 = m_state[2];
+        h3 = m_state[3];
+        h4 = m_state[4];
+        h5 = m_state[5];
+        h6 = m_state[6];
+        h7 = m_state[7];
+        h8 = m_state[8];
+        h9 = m_state[9];
+        h10 = m_state[10];
+        h11 = m_state[11];
+    }
+    m_length = length + m_length;
+
+    // if we've got anything stuffed away, use it now
+    if (m_remainder)
+    {
+        uint8_t prefix = sc_bufSize-m_remainder;
+        memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix);
+        u.p64 = m_data;
+        Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        u.p8 = ((const uint8_t *)message) + prefix;
+        length -= prefix;
+    }
+    else
+    {
+        u.p8 = (const uint8_t *)message;
+    }
+
+    // handle all whole blocks of sc_blockSize bytes
+    end = u.p64 + (length/sc_blockSize)*sc_numVars;
+    remainder = (uint8_t)(length-((const uint8_t *)end-u.p8));
+    if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
+    {
+        while (u.p64 < end)
+        {
+            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+    else
+    {
+        while (u.p64 < end)
+        {
+            memcpy(m_data, u.p8, sc_blockSize);
+            Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+            u.p64 += sc_numVars;
+        }
+    }
+
+    // stuff away the last few bytes
+    m_remainder = remainder;
+    memcpy(m_data, end, remainder);
+
+    // stuff away the variables
+    m_state[0] = h0;
+    m_state[1] = h1;
+    m_state[2] = h2;
+    m_state[3] = h3;
+    m_state[4] = h4;
+    m_state[5] = h5;
+    m_state[6] = h6;
+    m_state[7] = h7;
+    m_state[8] = h8;
+    m_state[9] = h9;
+    m_state[10] = h10;
+    m_state[11] = h11;
+}
+
+
+// report the hash for the concatenation of all message fragments so far
+void SpookyHashV2::Final(uint64_t *hash1, uint64_t *hash2)
+{
+    // init the variables
+    if (m_length < sc_bufSize)
+    {
+        *hash1 = m_state[0];
+        *hash2 = m_state[1];
+        Short( m_data, m_length, hash1, hash2);
+        return;
+    }
+
+    const uint64_t *data = (const uint64_t *)m_data;
+    uint8_t remainder = m_remainder;
+
+    uint64_t h0 = m_state[0];
+    uint64_t h1 = m_state[1];
+    uint64_t h2 = m_state[2];
+    uint64_t h3 = m_state[3];
+    uint64_t h4 = m_state[4];
+    uint64_t h5 = m_state[5];
+    uint64_t h6 = m_state[6];
+    uint64_t h7 = m_state[7];
+    uint64_t h8 = m_state[8];
+    uint64_t h9 = m_state[9];
+    uint64_t h10 = m_state[10];
+    uint64_t h11 = m_state[11];
+
+    if (remainder >= sc_blockSize)
+    {
+        // m_data can contain two blocks; handle any whole first block
+        Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        data += sc_numVars;
+        remainder -= sc_blockSize;
+    }
+
+    // mix in the last partial block, and the length mod sc_blockSize
+    memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder));
+
+    ((uint8_t *)data)[sc_blockSize-1] = remainder;
+
+    // do some final mixing
+    End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+
+    *hash1 = h0;
+    *hash2 = h1;
+}
+
+}  // namespace hash
+}  // namespace folly
diff --git a/faux-folly/folly/SpookyHashV2.h b/faux-folly/folly/SpookyHashV2.h
new file mode 100644
index 0000000..f32d8fe
--- /dev/null
+++ b/faux-folly/folly/SpookyHashV2.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// This is version 2 of SpookyHash, incompatible with version 1.
+//
+// SpookyHash: a 128-bit noncryptographic hash function
+// By Bob Jenkins, public domain
+//   Oct 31 2010: alpha, framework + SpookyHash::Mix appears right
+//   Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right
+//   Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas
+//   Feb  2 2012: production, same bits as beta
+//   Feb  5 2012: adjusted definitions of uint* to be more portable
+//   Mar 30 2012: 3 bytes/cycle, not 4.  Alpha was 4 but wasn't thorough enough.
+//   August 5 2012: SpookyV2 (different results)
+//
+// Up to 3 bytes/cycle for long messages.  Reasonably fast for short messages.
+// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.
+//
+// This was developed for and tested on 64-bit x86-compatible processors.
+// It assumes the processor is little-endian.  There is a macro
+// controlling whether unaligned reads are allowed (by default they are).
+// This should be an equally good hash on big-endian machines, but it will
+// compute different results on them than on little-endian machines.
+//
+// Google's CityHash has similar specs to SpookyHash, and CityHash is faster
+// on new Intel boxes.  MD4 and MD5 also have similar specs, but they are orders
+// of magnitude slower.  CRCs are two or more times slower, but unlike
+// SpookyHash, they have nice math for combining the CRCs of pieces to form
+// the CRCs of wholes.  There are also cryptographic hashes, but those are even
+// slower than MD5.
+//
+
+#ifndef FOLLY_SPOOKYHASHV2_H_
+#define FOLLY_SPOOKYHASHV2_H_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace folly {
+namespace hash {
+
+class SpookyHashV2
+{
+public:
+    //
+    // SpookyHash: hash a single message in one call, produce 128-bit output
+    //
+    static void Hash128(
+        const void *message,  // message to hash
+        size_t length,        // length of message in bytes
+        uint64_t *hash1,        // in/out: in seed 1, out hash value 1
+        uint64_t *hash2);       // in/out: in seed 2, out hash value 2
+
+    //
+    // Hash64: hash a single message in one call, return 64-bit output
+    //
+    static uint64_t Hash64(
+        const void *message,  // message to hash
+        size_t length,        // length of message in bytes
+        uint64_t seed)          // seed
+    {
+        uint64_t hash1 = seed;
+        Hash128(message, length, &hash1, &seed);
+        return hash1;
+    }
+
+    //
+    // Hash32: hash a single message in one call, produce 32-bit output
+    //
+    static uint32_t Hash32(
+        const void *message,  // message to hash
+        size_t length,        // length of message in bytes
+        uint32_t seed)          // seed
+    {
+        uint64_t hash1 = seed, hash2 = seed;
+        Hash128(message, length, &hash1, &hash2);
+        return (uint32_t)hash1;
+    }
+
+    //
+    // Init: initialize the context of a SpookyHash
+    //
+    void Init(
+        uint64_t seed1,       // any 64-bit value will do, including 0
+        uint64_t seed2);      // different seeds produce independent hashes
+
+    //
+    // Update: add a piece of a message to a SpookyHash state
+    //
+    void Update(
+        const void *message,  // message fragment
+        size_t length);       // length of message fragment in bytes
+
+
+    //
+    // Final: compute the hash for the current SpookyHash state
+    //
+    // This does not modify the state; you can keep updating it afterward
+    //
+    // The result is the same as if SpookyHash() had been called with
+    // all the pieces concatenated into one message.
+    //
+    void Final(
+        uint64_t *hash1,    // out only: first 64 bits of hash value.
+        uint64_t *hash2);   // out only: second 64 bits of hash value.
+
+    //
+    // left rotate a 64-bit value by k bytes
+    //
+    static inline uint64_t Rot64(uint64_t x, int k)
+    {
+        return (x << k) | (x >> (64 - k));
+    }
+
+    //
+    // This is used if the input is 96 bytes long or longer.
+    //
+    // The internal state is fully overwritten every 96 bytes.
+    // Every input bit appears to cause at least 128 bits of entropy
+    // before 96 other bytes are combined, when run forward or backward
+    //   For every input bit,
+    //   Two inputs differing in just that input bit
+    //   Where "differ" means xor or subtraction
+    //   And the base value is random
+    //   When run forward or backwards one Mix
+    // I tried 3 pairs of each; they all differed by at least 212 bits.
+    //
+    static inline void Mix(
+        const uint64_t *data,
+        uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3,
+        uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7,
+        uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11)
+    {
+      s0 += data[0];   s2 ^= s10; s11 ^= s0;  s0 = Rot64(s0,11);   s11 += s1;
+      s1 += data[1];   s3 ^= s11; s0 ^= s1;   s1 = Rot64(s1,32);   s0 += s2;
+      s2 += data[2];   s4 ^= s0;  s1 ^= s2;   s2 = Rot64(s2,43);   s1 += s3;
+      s3 += data[3];   s5 ^= s1;  s2 ^= s3;   s3 = Rot64(s3,31);   s2 += s4;
+      s4 += data[4];   s6 ^= s2;  s3 ^= s4;   s4 = Rot64(s4,17);   s3 += s5;
+      s5 += data[5];   s7 ^= s3;  s4 ^= s5;   s5 = Rot64(s5,28);   s4 += s6;
+      s6 += data[6];   s8 ^= s4;  s5 ^= s6;   s6 = Rot64(s6,39);   s5 += s7;
+      s7 += data[7];   s9 ^= s5;  s6 ^= s7;   s7 = Rot64(s7,57);   s6 += s8;
+      s8 += data[8];   s10 ^= s6; s7 ^= s8;   s8 = Rot64(s8,55);   s7 += s9;
+      s9 += data[9];   s11 ^= s7; s8 ^= s9;   s9 = Rot64(s9,54);   s8 += s10;
+      s10 += data[10]; s0 ^= s8;  s9 ^= s10;  s10 = Rot64(s10,22); s9 += s11;
+      s11 += data[11]; s1 ^= s9;  s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;
+    }
+
+    //
+    // Mix all 12 inputs together so that h0, h1 are a hash of them all.
+    //
+    // For two inputs differing in just the input bits
+    // Where "differ" means xor or subtraction
+    // And the base value is random, or a counting value starting at that bit
+    // The final result will have each bit of h0, h1 flip
+    // For every input bit,
+    // with probability 50 +- .3%
+    // For every pair of input bits,
+    // with probability 50 +- 3%
+    //
+    // This does not rely on the last Mix() call having already mixed some.
+    // Two iterations was almost good enough for a 64-bit result, but a
+    // 128-bit result is reported, so End() does three iterations.
+    //
+    static inline void EndPartial(
+        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,
+        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,
+        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)
+    {
+        h11+= h1;    h2 ^= h11;   h1 = Rot64(h1,44);
+        h0 += h2;    h3 ^= h0;    h2 = Rot64(h2,15);
+        h1 += h3;    h4 ^= h1;    h3 = Rot64(h3,34);
+        h2 += h4;    h5 ^= h2;    h4 = Rot64(h4,21);
+        h3 += h5;    h6 ^= h3;    h5 = Rot64(h5,38);
+        h4 += h6;    h7 ^= h4;    h6 = Rot64(h6,33);
+        h5 += h7;    h8 ^= h5;    h7 = Rot64(h7,10);
+        h6 += h8;    h9 ^= h6;    h8 = Rot64(h8,13);
+        h7 += h9;    h10^= h7;    h9 = Rot64(h9,38);
+        h8 += h10;   h11^= h8;    h10= Rot64(h10,53);
+        h9 += h11;   h0 ^= h9;    h11= Rot64(h11,42);
+        h10+= h0;    h1 ^= h10;   h0 = Rot64(h0,54);
+    }
+
+    static inline void End(
+        const uint64_t *data,
+        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,
+        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,
+        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)
+    {
+        h0 += data[0];   h1 += data[1];   h2 += data[2];   h3 += data[3];
+        h4 += data[4];   h5 += data[5];   h6 += data[6];   h7 += data[7];
+        h8 += data[8];   h9 += data[9];   h10 += data[10]; h11 += data[11];
+        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+    }
+
+    //
+    // The goal is for each bit of the input to expand into 128 bits of
+    //   apparent entropy before it is fully overwritten.
+    // n trials both set and cleared at least m bits of h0 h1 h2 h3
+    //   n: 2   m: 29
+    //   n: 3   m: 46
+    //   n: 4   m: 57
+    //   n: 5   m: 107
+    //   n: 6   m: 146
+    //   n: 7   m: 152
+    // when run forwards or backwards
+    // for all 1-bit and 2-bit diffs
+    // with diffs defined by either xor or subtraction
+    // with a base of all zeros plus a counter, or plus another bit, or random
+    //
+    static inline void ShortMix(uint64_t &h0, uint64_t &h1,
+                                uint64_t &h2, uint64_t &h3)
+    {
+        h2 = Rot64(h2,50);  h2 += h3;  h0 ^= h2;
+        h3 = Rot64(h3,52);  h3 += h0;  h1 ^= h3;
+        h0 = Rot64(h0,30);  h0 += h1;  h2 ^= h0;
+        h1 = Rot64(h1,41);  h1 += h2;  h3 ^= h1;
+        h2 = Rot64(h2,54);  h2 += h3;  h0 ^= h2;
+        h3 = Rot64(h3,48);  h3 += h0;  h1 ^= h3;
+        h0 = Rot64(h0,38);  h0 += h1;  h2 ^= h0;
+        h1 = Rot64(h1,37);  h1 += h2;  h3 ^= h1;
+        h2 = Rot64(h2,62);  h2 += h3;  h0 ^= h2;
+        h3 = Rot64(h3,34);  h3 += h0;  h1 ^= h3;
+        h0 = Rot64(h0,5);   h0 += h1;  h2 ^= h0;
+        h1 = Rot64(h1,36);  h1 += h2;  h3 ^= h1;
+    }
+
+    //
+    // Mix all 4 inputs together so that h0, h1 are a hash of them all.
+    //
+    // For two inputs differing in just the input bits
+    // Where "differ" means xor or subtraction
+    // And the base value is random, or a counting value starting at that bit
+    // The final result will have each bit of h0, h1 flip
+    // For every input bit,
+    // with probability 50 +- .3% (it is probably better than that)
+    // For every pair of input bits,
+    // with probability 50 +- .75% (the worst case is approximately that)
+    //
+    static inline void ShortEnd(uint64_t &h0, uint64_t &h1,
+                                uint64_t &h2, uint64_t &h3)
+    {
+        h3 ^= h2;  h2 = Rot64(h2,15);  h3 += h2;
+        h0 ^= h3;  h3 = Rot64(h3,52);  h0 += h3;
+        h1 ^= h0;  h0 = Rot64(h0,26);  h1 += h0;
+        h2 ^= h1;  h1 = Rot64(h1,51);  h2 += h1;
+        h3 ^= h2;  h2 = Rot64(h2,28);  h3 += h2;
+        h0 ^= h3;  h3 = Rot64(h3,9);   h0 += h3;
+        h1 ^= h0;  h0 = Rot64(h0,47);  h1 += h0;
+        h2 ^= h1;  h1 = Rot64(h1,54);  h2 += h1;
+        h3 ^= h2;  h2 = Rot64(h2,32);  h3 += h2;
+        h0 ^= h3;  h3 = Rot64(h3,25);  h0 += h3;
+        h1 ^= h0;  h0 = Rot64(h0,63);  h1 += h0;
+    }
+
+private:
+
+    //
+    // Short is used for messages under 192 bytes in length
+    // Short has a low startup cost, the normal mode is good for long
+    // keys, the cost crossover is at about 192 bytes.  The two modes were
+    // held to the same quality bar.
+    //
+    static void Short(
+        const void *message,  // message (byte array, not necessarily aligned)
+        size_t length,        // length of message (in bytes)
+        uint64_t *hash1,        // in/out: in the seed, out the hash value
+        uint64_t *hash2);       // in/out: in the seed, out the hash value
+
+    // number of uint64_t's in internal state
+    static const size_t sc_numVars = 12;
+
+    // size of the internal state
+    static const size_t sc_blockSize = sc_numVars*8;
+
+    // size of buffer of unhashed data, in bytes
+    static const size_t sc_bufSize = 2*sc_blockSize;
+
+    //
+    // sc_const: a constant which:
+    //  * is not zero
+    //  * is odd
+    //  * is a not-very-regular mix of 1's and 0's
+    //  * does not need any other special mathematical properties
+    //
+    static const uint64_t sc_const = 0xdeadbeefdeadbeefLL;
+
+    uint64_t m_data[2*sc_numVars];   // unhashed data, for partial messages
+    uint64_t m_state[sc_numVars];  // internal state of the hash
+    size_t m_length;             // total length of the input so far
+    uint8_t  m_remainder;          // length of unhashed data stashed in m_data
+};
+
+}  // namespace hash
+}  // namespace folly
+
+#endif
diff --git a/faux-folly/folly/String-inl.h b/faux-folly/folly/String-inl.h
new file mode 100644
index 0000000..1a4edb7
--- /dev/null
+++ b/faux-folly/folly/String-inl.h
@@ -0,0 +1,673 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_STRING_INL_H_
+#define FOLLY_STRING_INL_H_
+
+#include <stdexcept>
+#include <iterator>
+
+#ifndef FOLLY_BASE_STRING_H_
+#error This file may only be included from String.h
+#endif
+
+namespace folly {
+
+namespace detail {
+// Map from character code to value of one-character escape sequence
+// ('\n' = 10 maps to 'n'), 'O' if the character should be printed as
+// an octal escape sequence, or 'P' if the character is printable and
+// should be printed as is.
+extern const char cEscapeTable[];
+}  // namespace detail
+
+template <class String>
+void cEscape(StringPiece str, String& out) {
+  char esc[4];
+  esc[0] = '\\';
+  out.reserve(out.size() + str.size());
+  auto p = str.begin();
+  auto last = p;  // last regular character
+  // We advance over runs of regular characters (printable, not double-quote or
+  // backslash) and copy them in one go; this is faster than calling push_back
+  // repeatedly.
+  while (p != str.end()) {
+    char c = *p;
+    unsigned char v = static_cast<unsigned char>(c);
+    char e = detail::cEscapeTable[v];
+    if (e == 'P') {  // printable
+      ++p;
+    } else if (e == 'O') {  // octal
+      out.append(&*last, p - last);
+      esc[1] = '0' + ((v >> 6) & 7);
+      esc[2] = '0' + ((v >> 3) & 7);
+      esc[3] = '0' + (v & 7);
+      out.append(esc, 4);
+      ++p;
+      last = p;
+    } else {  // special 1-character escape
+      out.append(&*last, p - last);
+      esc[1] = e;
+      out.append(esc, 2);
+      ++p;
+      last = p;
+    }
+  }
+  out.append(&*last, p - last);
+}
+
+namespace detail {
+// Map from the character code of the character following a backslash to
+// the unescaped character if a valid one-character escape sequence
+// ('n' maps to 10 = '\n'), 'O' if this is the first character of an
+// octal escape sequence, 'X' if this is the first character of a
+// hexadecimal escape sequence, or 'I' if this escape sequence is invalid.
+extern const char cUnescapeTable[];
+
+// Map from the character code to the hex value, or 16 if invalid hex char.
+extern const unsigned char hexTable[];
+}  // namespace detail
+
+template <class String>
+void cUnescape(StringPiece str, String& out, bool strict) {
+  out.reserve(out.size() + str.size());
+  auto p = str.begin();
+  auto last = p;  // last regular character (not part of an escape sequence)
+  // We advance over runs of regular characters (not backslash) and copy them
+  // in one go; this is faster than calling push_back repeatedly.
+  while (p != str.end()) {
+    char c = *p;
+    if (c != '\\') {  // normal case
+      ++p;
+      continue;
+    }
+    out.append(&*last, p - last);
+    if (p == str.end()) {  // backslash at end of string
+      if (strict) {
+        throw std::invalid_argument("incomplete escape sequence");
+      }
+      out.push_back('\\');
+      last = p;
+      continue;
+    }
+    ++p;
+    char e = detail::cUnescapeTable[static_cast<unsigned char>(*p)];
+    if (e == 'O') {  // octal
+      unsigned char val = 0;
+      for (int i = 0; i < 3 && p != str.end() && *p >= '0' && *p <= '7';
+           ++i, ++p) {
+        val = (val << 3) | (*p - '0');
+      }
+      out.push_back(val);
+      last = p;
+    } else if (e == 'X') {  // hex
+      ++p;
+      if (p == str.end()) {  // \x at end of string
+        if (strict) {
+          throw std::invalid_argument("incomplete hex escape sequence");
+        }
+        out.append("\\x");
+        last = p;
+        continue;
+      }
+      unsigned char val = 0;
+      unsigned char h;
+      for (; (p != str.end() &&
+              (h = detail::hexTable[static_cast<unsigned char>(*p)]) < 16);
+           ++p) {
+        val = (val << 4) | h;
+      }
+      out.push_back(val);
+      last = p;
+    } else if (e == 'I') {  // invalid
+      if (strict) {
+        throw std::invalid_argument("invalid escape sequence");
+      }
+      out.push_back('\\');
+      out.push_back(*p);
+      ++p;
+      last = p;
+    } else {  // standard escape sequence, \' etc
+      out.push_back(e);
+      ++p;
+      last = p;
+    }
+  }
+  out.append(&*last, p - last);
+}
+
+namespace detail {
+// Map from character code to escape mode:
+// 0 = pass through
+// 1 = unused
+// 2 = pass through in PATH mode
+// 3 = space, replace with '+' in QUERY mode
+// 4 = percent-encode
+extern const unsigned char uriEscapeTable[];
+}  // namespace detail
+
+template <class String>
+void uriEscape(StringPiece str, String& out, UriEscapeMode mode) {
+  static const char hexValues[] = "0123456789abcdef";
+  char esc[3];
+  esc[0] = '%';
+  // Preallocate assuming that 25% of the input string will be escaped
+  out.reserve(out.size() + str.size() + 3 * (str.size() / 4));
+  auto p = str.begin();
+  auto last = p;  // last regular character
+  // We advance over runs of passthrough characters and copy them in one go;
+  // this is faster than calling push_back repeatedly.
+  unsigned char minEncode = static_cast<unsigned char>(mode);
+  while (p != str.end()) {
+    char c = *p;
+    unsigned char v = static_cast<unsigned char>(c);
+    unsigned char discriminator = detail::uriEscapeTable[v];
+    if (LIKELY(discriminator <= minEncode)) {
+      ++p;
+    } else if (mode == UriEscapeMode::QUERY && discriminator == 3) {
+      out.append(&*last, p - last);
+      out.push_back('+');
+      ++p;
+      last = p;
+    } else {
+      out.append(&*last, p - last);
+      esc[1] = hexValues[v >> 4];
+      esc[2] = hexValues[v & 0x0f];
+      out.append(esc, 3);
+      ++p;
+      last = p;
+    }
+  }
+  out.append(&*last, p - last);
+}
+
+template <class String>
+void uriUnescape(StringPiece str, String& out, UriEscapeMode mode) {
+  out.reserve(out.size() + str.size());
+  auto p = str.begin();
+  auto last = p;
+  // We advance over runs of passthrough characters and copy them in one go;
+  // this is faster than calling push_back repeatedly.
+  while (p != str.end()) {
+    char c = *p;
+    switch (c) {
+    case '%':
+      {
+        if (UNLIKELY(std::distance(p, str.end()) < 3)) {
+          throw std::invalid_argument("incomplete percent encode sequence");
+        }
+        auto h1 = detail::hexTable[static_cast<unsigned char>(p[1])];
+        auto h2 = detail::hexTable[static_cast<unsigned char>(p[2])];
+        if (UNLIKELY(h1 == 16 || h2 == 16)) {
+          throw std::invalid_argument("invalid percent encode sequence");
+        }
+        out.append(&*last, p - last);
+        out.push_back((h1 << 4) | h2);
+        p += 3;
+        last = p;
+        break;
+      }
+    case '+':
+      if (mode == UriEscapeMode::QUERY) {
+        out.append(&*last, p - last);
+        out.push_back(' ');
+        ++p;
+        last = p;
+        break;
+      }
+      // else fallthrough
+    default:
+      ++p;
+      break;
+    }
+  }
+  out.append(&*last, p - last);
+}
+
+namespace detail {
+
+/*
+ * The following functions are type-overloaded helpers for
+ * internalSplit().
+ */
+inline size_t delimSize(char)          { return 1; }
+inline size_t delimSize(StringPiece s) { return s.size(); }
+inline bool atDelim(const char* s, char c) {
+ return *s == c;
+}
+inline bool atDelim(const char* s, StringPiece sp) {
+  return !std::memcmp(s, sp.start(), sp.size());
+}
+
+// These are used to short-circuit internalSplit() in the case of
+// 1-character strings.
+inline char delimFront(char c) {
+  // This one exists only for compile-time; it should never be called.
+  std::abort();
+  return c;
+}
+inline char delimFront(StringPiece s) {
+  assert(!s.empty() && s.start() != nullptr);
+  return *s.start();
+}
+
+/*
+ * These output conversion templates allow us to support multiple
+ * output string types, even when we are using an arbitrary
+ * OutputIterator.
+ */
+template<class OutStringT> struct OutputConverter {};
+
+template<> struct OutputConverter<std::string> {
+  std::string operator()(StringPiece sp) const {
+    return sp.toString();
+  }
+};
+
+template<> struct OutputConverter<fbstring> {
+  fbstring operator()(StringPiece sp) const {
+    return sp.toFbstring();
+  }
+};
+
+template<> struct OutputConverter<StringPiece> {
+  StringPiece operator()(StringPiece sp) const { return sp; }
+};
+
+/*
+ * Shared implementation for all the split() overloads.
+ *
+ * This uses some external helpers that are overloaded to let this
+ * algorithm be more performant if the deliminator is a single
+ * character instead of a whole string.
+ *
+ * @param ignoreEmpty iff true, don't copy empty segments to output
+ */
+template<class OutStringT, class DelimT, class OutputIterator>
+void internalSplit(DelimT delim, StringPiece sp, OutputIterator out,
+    bool ignoreEmpty) {
+  assert(sp.empty() || sp.start() != nullptr);
+
+  const char* s = sp.start();
+  const size_t strSize = sp.size();
+  const size_t dSize = delimSize(delim);
+
+  OutputConverter<OutStringT> conv;
+
+  if (dSize > strSize || dSize == 0) {
+    if (!ignoreEmpty || strSize > 0) {
+      *out++ = conv(sp);
+    }
+    return;
+  }
+  if (boost::is_same<DelimT,StringPiece>::value && dSize == 1) {
+    // Call the char version because it is significantly faster.
+    return internalSplit<OutStringT>(delimFront(delim), sp, out,
+      ignoreEmpty);
+  }
+
+  size_t tokenStartPos = 0;
+  size_t tokenSize = 0;
+  for (size_t i = 0; i <= strSize - dSize; ++i) {
+    if (atDelim(&s[i], delim)) {
+      if (!ignoreEmpty || tokenSize > 0) {
+        *out++ = conv(StringPiece(&s[tokenStartPos], tokenSize));
+      }
+
+      tokenStartPos = i + dSize;
+      tokenSize = 0;
+      i += dSize - 1;
+    } else {
+      ++tokenSize;
+    }
+  }
+  tokenSize = strSize - tokenStartPos;
+  if (!ignoreEmpty || tokenSize > 0) {
+    *out++ = conv(StringPiece(&s[tokenStartPos], tokenSize));
+  }
+}
+
+template<class String> StringPiece prepareDelim(const String& s) {
+  return StringPiece(s);
+}
+inline char prepareDelim(char c) { return c; }
+
+template <class Dst>
+struct convertTo {
+  template <class Src>
+  static Dst from(const Src& src) { return folly::to<Dst>(src); }
+  static Dst from(const Dst& src) { return src; }
+};
+
+template<bool exact,
+         class Delim,
+         class OutputType>
+typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
+splitFixed(const Delim& delimiter,
+           StringPiece input,
+           OutputType& out) {
+  if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) {
+    return false;
+  }
+  out = convertTo<OutputType>::from(input);
+  return true;
+}
+
+template<bool exact,
+         class Delim,
+         class OutputType,
+         class... OutputTypes>
+typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
+splitFixed(const Delim& delimiter,
+           StringPiece input,
+           OutputType& outHead,
+           OutputTypes&... outTail) {
+  size_t cut = input.find(delimiter);
+  if (UNLIKELY(cut == std::string::npos)) {
+    return false;
+  }
+  StringPiece head(input.begin(), input.begin() + cut);
+  StringPiece tail(input.begin() + cut + detail::delimSize(delimiter),
+                   input.end());
+  if (LIKELY(splitFixed<exact>(delimiter, tail, outTail...))) {
+    outHead = convertTo<OutputType>::from(head);
+    return true;
+  }
+  return false;
+}
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+template<class Delim, class String, class OutputType>
+void split(const Delim& delimiter,
+           const String& input,
+           std::vector<OutputType>& out,
+           bool ignoreEmpty) {
+  detail::internalSplit<OutputType>(
+    detail::prepareDelim(delimiter),
+    StringPiece(input),
+    std::back_inserter(out),
+    ignoreEmpty);
+}
+
+template<class Delim, class String, class OutputType>
+void split(const Delim& delimiter,
+           const String& input,
+           fbvector<OutputType>& out,
+           bool ignoreEmpty) {
+  detail::internalSplit<OutputType>(
+    detail::prepareDelim(delimiter),
+    StringPiece(input),
+    std::back_inserter(out),
+    ignoreEmpty);
+}
+
+template<class OutputValueType, class Delim, class String,
+         class OutputIterator>
+void splitTo(const Delim& delimiter,
+             const String& input,
+             OutputIterator out,
+             bool ignoreEmpty) {
+  detail::internalSplit<OutputValueType>(
+    detail::prepareDelim(delimiter),
+    StringPiece(input),
+    out,
+    ignoreEmpty);
+}
+
+template<bool exact,
+         class Delim,
+         class OutputType,
+         class... OutputTypes>
+typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
+split(const Delim& delimiter,
+      StringPiece input,
+      OutputType& outHead,
+      OutputTypes&... outTail) {
+  return detail::splitFixed<exact>(
+    detail::prepareDelim(delimiter),
+    input,
+    outHead,
+    outTail...);
+}
+
+namespace detail {
+
+/*
+ * If a type can have its string size determined cheaply, we can more
+ * efficiently append it in a loop (see internalJoinAppend). Note that the
+ * struct need not conform to the std::string api completely (ex. does not need
+ * to implement append()).
+ */
+template <class T> struct IsSizableString {
+  enum { value = IsSomeString<T>::value
+         || std::is_same<T, StringPiece>::value };
+};
+
+template <class Iterator>
+struct IsSizableStringContainerIterator :
+  IsSizableString<typename std::iterator_traits<Iterator>::value_type> {
+};
+
+template <class Delim, class Iterator, class String>
+void internalJoinAppend(Delim delimiter,
+                        Iterator begin,
+                        Iterator end,
+                        String& output) {
+  assert(begin != end);
+  if (std::is_same<Delim, StringPiece>::value &&
+      delimSize(delimiter) == 1) {
+    internalJoinAppend(delimFront(delimiter), begin, end, output);
+    return;
+  }
+  toAppend(*begin, &output);
+  while (++begin != end) {
+    toAppend(delimiter, *begin, &output);
+  }
+}
+
+template <class Delim, class Iterator, class String>
+typename std::enable_if<IsSizableStringContainerIterator<Iterator>::value>::type
+internalJoin(Delim delimiter,
+             Iterator begin,
+             Iterator end,
+             String& output) {
+  output.clear();
+  if (begin == end) {
+    return;
+  }
+  const size_t dsize = delimSize(delimiter);
+  Iterator it = begin;
+  size_t size = it->size();
+  while (++it != end) {
+    size += dsize + it->size();
+  }
+  output.reserve(size);
+  internalJoinAppend(delimiter, begin, end, output);
+}
+
+template <class Delim, class Iterator, class String>
+typename
+std::enable_if<!IsSizableStringContainerIterator<Iterator>::value>::type
+internalJoin(Delim delimiter,
+             Iterator begin,
+             Iterator end,
+             String& output) {
+  output.clear();
+  if (begin == end) {
+    return;
+  }
+  internalJoinAppend(delimiter, begin, end, output);
+}
+
+}  // namespace detail
+
+template <class Delim, class Iterator, class String>
+void join(const Delim& delimiter,
+          Iterator begin,
+          Iterator end,
+          String& output) {
+  detail::internalJoin(
+    detail::prepareDelim(delimiter),
+    begin,
+    end,
+    output);
+}
+
+template <class String1, class String2>
+void backslashify(const String1& input, String2& output, bool hex_style) {
+  static const char hexValues[] = "0123456789abcdef";
+  output.clear();
+  output.reserve(3 * input.size());
+  for (unsigned char c : input) {
+    // less than space or greater than '~' are considered unprintable
+    if (c < 0x20 || c > 0x7e || c == '\\') {
+      bool hex_append = false;
+      output.push_back('\\');
+      if (hex_style) {
+        hex_append = true;
+      } else {
+        if (c == '\r') output += 'r';
+        else if (c == '\n') output += 'n';
+        else if (c == '\t') output += 't';
+        else if (c == '\a') output += 'a';
+        else if (c == '\b') output += 'b';
+        else if (c == '\0') output += '0';
+        else if (c == '\\') output += '\\';
+        else {
+          hex_append = true;
+        }
+      }
+      if (hex_append) {
+        output.push_back('x');
+        output.push_back(hexValues[(c >> 4) & 0xf]);
+        output.push_back(hexValues[c & 0xf]);
+      }
+    } else {
+      output += c;
+    }
+  }
+}
+
+template <class String1, class String2>
+void humanify(const String1& input, String2& output) {
+  size_t numUnprintable = 0;
+  size_t numPrintablePrefix = 0;
+  for (unsigned char c : input) {
+    if (c < 0x20 || c > 0x7e || c == '\\') {
+      ++numUnprintable;
+    }
+    if (numUnprintable == 0) {
+      ++numPrintablePrefix;
+    }
+  }
+
+  // hexlify doubles a string's size; backslashify can potentially
+  // explode it by 4x.  Now, the printable range of the ascii
+  // "spectrum" is around 95 out of 256 values, so a "random" binary
+  // string should be around 60% unprintable.  We use a 50% hueristic
+  // here, so if a string is 60% unprintable, then we just use hex
+  // output.  Otherwise we backslash.
+  //
+  // UTF8 is completely ignored; as a result, utf8 characters will
+  // likely be \x escaped (since most common glyphs fit in two bytes).
+  // This is a tradeoff of complexity/speed instead of a convenience
+  // that likely would rarely matter.  Moreover, this function is more
+  // about displaying underlying bytes, not about displaying glyphs
+  // from languages.
+  if (numUnprintable == 0) {
+    output = input;
+  } else if (5 * numUnprintable >= 3 * input.size()) {
+    // However!  If we have a "meaningful" prefix of printable
+    // characters, say 20% of the string, we backslashify under the
+    // assumption viewing the prefix as ascii is worth blowing the
+    // output size up a bit.
+    if (5 * numPrintablePrefix >= input.size()) {
+      backslashify(input, output);
+    } else {
+      output = "0x";
+      hexlify(input, output, true /* append output */);
+    }
+  } else {
+    backslashify(input, output);
+  }
+}
+
+template<class InputString, class OutputString>
+bool hexlify(const InputString& input, OutputString& output,
+             bool append_output) {
+  if (!append_output) output.clear();
+
+  static char hexValues[] = "0123456789abcdef";
+  auto j = output.size();
+  output.resize(2 * input.size() + output.size());
+  for (size_t i = 0; i < input.size(); ++i) {
+    int ch = input[i];
+    output[j++] = hexValues[(ch >> 4) & 0xf];
+    output[j++] = hexValues[ch & 0xf];
+  }
+  return true;
+}
+
+template<class InputString, class OutputString>
+bool unhexlify(const InputString& input, OutputString& output) {
+  if (input.size() % 2 != 0) {
+    return false;
+  }
+  output.resize(input.size() / 2);
+  int j = 0;
+  auto unhex = [](char c) -> int {
+    return c >= '0' && c <= '9' ? c - '0' :
+           c >= 'A' && c <= 'F' ? c - 'A' + 10 :
+           c >= 'a' && c <= 'f' ? c - 'a' + 10 :
+           -1;
+  };
+
+  for (size_t i = 0; i < input.size(); i += 2) {
+    int highBits = unhex(input[i]);
+    int lowBits = unhex(input[i + 1]);
+    if (highBits < 0 || lowBits < 0) {
+      return false;
+    }
+    output[j++] = (highBits << 4) + lowBits;
+  }
+  return true;
+}
+
+namespace detail {
+/**
+ * Hex-dump at most 16 bytes starting at offset from a memory area of size
+ * bytes.  Return the number of bytes actually dumped.
+ */
+size_t hexDumpLine(const void* ptr, size_t offset, size_t size,
+                   std::string& line);
+}  // namespace detail
+
+template <class OutIt>
+void hexDump(const void* ptr, size_t size, OutIt out) {
+  size_t offset = 0;
+  std::string line;
+  while (offset < size) {
+    offset += detail::hexDumpLine(ptr, offset, size, line);
+    *out++ = line;
+  }
+}
+
+}  // namespace folly
+
+#endif /* FOLLY_STRING_INL_H_ */
diff --git a/faux-folly/folly/String.cpp b/faux-folly/folly/String.cpp
new file mode 100644
index 0000000..706c1ea
--- /dev/null
+++ b/faux-folly/folly/String.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2015 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/String.h>
+
+#include <folly/Format.h>
+#include <folly/ScopeGuard.h>
+
+#include <cerrno>
+#include <cstdarg>
+#include <cstring>
+#include <stdexcept>
+#include <iterator>
+#include <cctype>
+#include <string.h>
+#include <glog/logging.h>
+
+namespace folly {
+
+namespace {
+
+int stringAppendfImplHelper(char* buf,
+                            size_t bufsize,
+                            const char* format,
+                            va_list args) {
+  va_list args_copy;
+  va_copy(args_copy, args);
+  int bytes_used = vsnprintf(buf, bufsize, format, args_copy);
+  va_end(args_copy);
+  return bytes_used;
+}
+
+void stringAppendfImpl(std::string& output, const char* format, va_list args) {
+  // Very simple; first, try to avoid an allocation by using an inline
+  // buffer.  If that fails to hold the output string, allocate one on
+  // the heap, use it instead.
+  //
+  // It is hard to guess the proper size of this buffer; some
+  // heuristics could be based on the number of format characters, or
+  // static analysis of a codebase.  Or, we can just pick a number
+  // that seems big enough for simple cases (say, one line of text on
+  // a terminal) without being large enough to be concerning as a
+  // stack variable.
+  std::array<char, 128> inline_buffer;
+
+  int bytes_used = stringAppendfImplHelper(
+      inline_buffer.data(), inline_buffer.size(), format, args);
+  if (bytes_used < 0) {
+    throw std::runtime_error(to<std::string>(
+        "Invalid format string; snprintf returned negative "
+        "with format string: ",
+        format));
+  }
+
+  if (static_cast<size_t>(bytes_used) < inline_buffer.size()) {
+    output.append(inline_buffer.data(), bytes_used);
+    return;
+  }
+
+  // Couldn't fit.  Heap allocate a buffer, oh well.
+  std::unique_ptr<char[]> heap_buffer(new char[bytes_used + 1]);
+  int final_bytes_used =
+      stringAppendfImplHelper(heap_buffer.get(), bytes_used + 1, format, args);
+  // The second call can take fewer bytes if, for example, we were printing a
+  // string buffer with null-terminating char using a width specifier -
+  // vsnprintf("%.*s", buf.size(), buf)
+  CHECK(bytes_used >= final_bytes_used);
+
+  // We don't keep the trailing '\0' in our output string
+  output.append(heap_buffer.get(), final_bytes_used);
+}
+
+} // anon namespace
+
+std::string stringPrintf(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  SCOPE_EXIT {
+    va_end(ap);
+  };
+  return stringVPrintf(format, ap);
+}
+
+std::string stringVPrintf(const char* format, va_list ap) {
+  std::string ret;
+  stringAppendfImpl(ret, format, ap);
+  return ret;
+}
+
+// Basic declarations; allow for parameters of strings and string
+// pieces to be specified.
+std::string& stringAppendf(std::string* output, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  SCOPE_EXIT {
+    va_end(ap);
+  };
+  return stringVAppendf(output, format, ap);
+}
+
+std::string& stringVAppendf(std::string* output,
+                            const char* format,
+                            va_list ap) {
+  stringAppendfImpl(*output, format, ap);
+  return *output;
+}
+
+void stringPrintf(std::string* output, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  SCOPE_EXIT {
+    va_end(ap);
+  };
+  return stringVPrintf(output, format, ap);
+}
+
+void stringVPrintf(std::string* output, const char* format, va_list ap) {
+  output->clear();
+  stringAppendfImpl(*output, format, ap);
+};
+
+namespace {
+
+struct PrettySuffix {
+  const char* suffix;
+  double val;
+};
+
+const PrettySuffix kPrettyTimeSuffixes[] = {
+  { "s ", 1e0L },
+  { "ms", 1e-3L },
+  { "us", 1e-6L },
+  { "ns", 1e-9L },
+  { "ps", 1e-12L },
+  { "s ", 0 },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettyBytesMetricSuffixes[] = {
+  { "TB", 1e12L },
+  { "GB", 1e9L },
+  { "MB", 1e6L },
+  { "kB", 1e3L },
+  { "B ", 0L },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettyBytesBinarySuffixes[] = {
+  { "TB", int64_t(1) << 40 },
+  { "GB", int64_t(1) << 30 },
+  { "MB", int64_t(1) << 20 },
+  { "kB", int64_t(1) << 10 },
+  { "B ", 0L },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettyBytesBinaryIECSuffixes[] = {
+  { "TiB", int64_t(1) << 40 },
+  { "GiB", int64_t(1) << 30 },
+  { "MiB", int64_t(1) << 20 },
+  { "KiB", int64_t(1) << 10 },
+  { "B  ", 0L },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettyUnitsMetricSuffixes[] = {
+  { "tril", 1e12L },
+  { "bil",  1e9L },
+  { "M",    1e6L },
+  { "k",    1e3L },
+  { " ",      0  },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettyUnitsBinarySuffixes[] = {
+  { "T", int64_t(1) << 40 },
+  { "G", int64_t(1) << 30 },
+  { "M", int64_t(1) << 20 },
+  { "k", int64_t(1) << 10 },
+  { " ", 0 },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettyUnitsBinaryIECSuffixes[] = {
+  { "Ti", int64_t(1) << 40 },
+  { "Gi", int64_t(1) << 30 },
+  { "Mi", int64_t(1) << 20 },
+  { "Ki", int64_t(1) << 10 },
+  { "  ", 0 },
+  { 0, 0 },
+};
+
+const PrettySuffix kPrettySISuffixes[] = {
+  { "Y", 1e24L },
+  { "Z", 1e21L },
+  { "E", 1e18L },
+  { "P", 1e15L },
+  { "T", 1e12L },
+  { "G", 1e9L },
+  { "M", 1e6L },
+  { "k", 1e3L },
+  { "h", 1e2L },
+  { "da", 1e1L },
+  { "d", 1e-1L },
+  { "c", 1e-2L },
+  { "m", 1e-3L },
+  { "u", 1e-6L },
+  { "n", 1e-9L },
+  { "p", 1e-12L },
+  { "f", 1e-15L },
+  { "a", 1e-18L },
+  { "z", 1e-21L },
+  { "y", 1e-24L },
+  { " ", 0 },
+  { 0, 0}
+};
+
+const PrettySuffix* const kPrettySuffixes[PRETTY_NUM_TYPES] = {
+  kPrettyTimeSuffixes,
+  kPrettyBytesMetricSuffixes,
+  kPrettyBytesBinarySuffixes,
+  kPrettyBytesBinaryIECSuffixes,
+  kPrettyUnitsMetricSuffixes,
+  kPrettyUnitsBinarySuffixes,
+  kPrettyUnitsBinaryIECSuffixes,
+  kPrettySISuffixes,
+};
+
+}  // namespace
+
+std::string prettyPrint(double val, PrettyType type, bool addSpace) {
+  char buf[100];
+
+  // pick the suffixes to use
+  assert(type >= 0);
+  assert(type < PRETTY_NUM_TYPES);
+  const PrettySuffix* suffixes = kPrettySuffixes[type];
+
+  // find the first suffix we're bigger than -- then use it
+  double abs_val = fabs(val);
+  for (int i = 0; suffixes[i].suffix; ++i) {
+    if (abs_val >= suffixes[i].val) {
+      snprintf(buf, sizeof buf, "%.4g%s%s",
+               (suffixes[i].val ? (val / suffixes[i].val)
+                                : val),
+               (addSpace ? " " : ""),
+               suffixes[i].suffix);
+      return std::string(buf);
+    }
+  }
+
+  // no suffix, we've got a tiny value -- just print it in sci-notation
+  snprintf(buf, sizeof buf, "%.4g", val);
+  return std::string(buf);
+}
+
+//TODO:
+//1) Benchmark & optimize
+double prettyToDouble(folly::StringPiece *const prettyString,
+                      const PrettyType type) {
+  double value = folly::to<double>(prettyString);
+  while (prettyString->size() > 0 && std::isspace(prettyString->front())) {
+    prettyString->advance(1); //Skipping spaces between number and suffix
+  }
+  const PrettySuffix* suffixes = kPrettySuffixes[type];
+  int longestPrefixLen = -1;
+  int bestPrefixId = -1;
+  for (int j = 0 ; suffixes[j].suffix; ++j) {
+    if (suffixes[j].suffix[0] == ' '){//Checking for " " -> number rule.
+      if (longestPrefixLen == -1) {
+        longestPrefixLen = 0; //No characters to skip
+        bestPrefixId = j;
+      }
+    } else if (prettyString->startsWith(suffixes[j].suffix)) {
+      int suffixLen = strlen(suffixes[j].suffix);
+      //We are looking for a longest suffix matching prefix of the string
+      //after numeric value. We need this in case suffixes have common prefix.
+      if (suffixLen > longestPrefixLen) {
+        longestPrefixLen = suffixLen;
+        bestPrefixId = j;
+      }
+    }
+  }
+  if (bestPrefixId == -1) { //No valid suffix rule found
+    throw std::invalid_argument(folly::to<std::string>(
+            "Unable to parse suffix \"",
+            prettyString->toString(), "\""));
+  }
+  prettyString->advance(longestPrefixLen);
+  return suffixes[bestPrefixId].val ? value * suffixes[bestPrefixId].val :
+                                      value;
+}
+
+double prettyToDouble(folly::StringPiece prettyString, const PrettyType type){
+  double result = prettyToDouble(&prettyString, type);
+  detail::enforceWhitespace(prettyString.data(),
+                            prettyString.data() + prettyString.size());
+  return result;
+}
+
+std::string hexDump(const void* ptr, size_t size) {
+  std::ostringstream os;
+  hexDump(ptr, size, std::ostream_iterator<StringPiece>(os, "\n"));
+  return os.str();
+}
+
+fbstring errnoStr(int err) {
+  int savedErrno = errno;
+
+  // Ensure that we reset errno upon exit.
+  auto guard(makeGuard([&] { errno = savedErrno; }));
+
+  char buf[1024];
+  buf[0] = '\0';
+
+  fbstring result;
+
+  // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/strerror_r.3.html
+  // http://www.kernel.org/doc/man-pages/online/pages/man3/strerror.3.html
+#if defined(_WIN32) && (defined(__MINGW32__) || defined(_MSC_VER))
+  // mingw64 has no strerror_r, but Windows has strerror_s, which C11 added
+  // as well. So maybe we should use this across all platforms (together
+  // with strerrorlen_s). Note strerror_r and _s have swapped args.
+  int r = strerror_s(buf, sizeof(buf), err);
+  if (r != 0) {
+    result = to<fbstring>(
+      "Unknown error ", err,
+      " (strerror_r failed with error ", errno, ")");
+  } else {
+    result.assign(buf);
+  }
+#elif defined(FOLLY_HAVE_XSI_STRERROR_R) || \
+  defined(__APPLE__)
+  // Using XSI-compatible strerror_r
+  int r = strerror_r(err, buf, sizeof(buf));
+
+  // OSX/FreeBSD use EINVAL and Linux uses -1 so just check for non-zero
+  if (r != 0) {
+    result = to<fbstring>(
+      "Unknown error ", err,
+      " (strerror_r failed with error ", errno, ")");
+  } else {
+    result.assign(buf);
+  }
+#else
+  // Using GNU strerror_r
+  result.assign(strerror_r(err, buf, sizeof(buf)));
+#endif
+
+  return result;
+}
+
+namespace {
+
+void toLowerAscii8(char& c) {
+  // Branchless tolower, based on the input-rotating trick described
+  // at http://www.azillionmonkeys.com/qed/asmexample.html
+  //
+  // This algorithm depends on an observation: each uppercase
+  // ASCII character can be converted to its lowercase equivalent
+  // by adding 0x20.
+
+  // Step 1: Clear the high order bit. We'll deal with it in Step 5.
+  unsigned char rotated = c & 0x7f;
+  // Currently, the value of rotated, as a function of the original c is:
+  //   below 'A':   0- 64
+  //   'A'-'Z':    65- 90
+  //   above 'Z':  91-127
+
+  // Step 2: Add 0x25 (37)
+  rotated += 0x25;
+  // Now the value of rotated, as a function of the original c is:
+  //   below 'A':   37-101
+  //   'A'-'Z':    102-127
+  //   above 'Z':  128-164
+
+  // Step 3: clear the high order bit
+  rotated &= 0x7f;
+  //   below 'A':   37-101
+  //   'A'-'Z':    102-127
+  //   above 'Z':    0- 36
+
+  // Step 4: Add 0x1a (26)
+  rotated += 0x1a;
+  //   below 'A':   63-127
+  //   'A'-'Z':    128-153
+  //   above 'Z':   25- 62
+
+  // At this point, note that only the uppercase letters have been
+  // transformed into values with the high order bit set (128 and above).
+
+  // Step 5: Shift the high order bit 2 spaces to the right: the spot
+  // where the only 1 bit in 0x20 is.  But first, how we ignored the
+  // high order bit of the original c in step 1?  If that bit was set,
+  // we may have just gotten a false match on a value in the range
+  // 128+'A' to 128+'Z'.  To correct this, need to clear the high order
+  // bit of rotated if the high order bit of c is set.  Since we don't
+  // care about the other bits in rotated, the easiest thing to do
+  // is invert all the bits in c and bitwise-and them with rotated.
+  rotated &= ~c;
+  rotated >>= 2;
+
+  // Step 6: Apply a mask to clear everything except the 0x20 bit
+  // in rotated.
+  rotated &= 0x20;
+
+  // At this point, rotated is 0x20 if c is 'A'-'Z' and 0x00 otherwise
+
+  // Step 7: Add rotated to c
+  c += rotated;
+}
+
+void toLowerAscii32(uint32_t& c) {
+  // Besides being branchless, the algorithm in toLowerAscii8() has another
+  // interesting property: None of the addition operations will cause
+  // an overflow in the 8-bit value.  So we can pack four 8-bit values
+  // into a uint32_t and run each operation on all four values in parallel
+  // without having to use any CPU-specific SIMD instructions.
+  uint32_t rotated = c & uint32_t(0x7f7f7f7fL);
+  rotated += uint32_t(0x25252525L);
+  rotated &= uint32_t(0x7f7f7f7fL);
+  rotated += uint32_t(0x1a1a1a1aL);
+
+  // Step 5 involves a shift, so some bits will spill over from each
+  // 8-bit value into the next.  But that's okay, because they're bits
+  // that will be cleared by the mask in step 6 anyway.
+  rotated &= ~c;
+  rotated >>= 2;
+  rotated &= uint32_t(0x20202020L);
+  c += rotated;
+}
+
+void toLowerAscii64(uint64_t& c) {
+  // 64-bit version of toLower32
+  uint64_t rotated = c & uint64_t(0x7f7f7f7f7f7f7f7fL);
+  rotated += uint64_t(0x2525252525252525L);
+  rotated &= uint64_t(0x7f7f7f7f7f7f7f7fL);
+  rotated += uint64_t(0x1a1a1a1a1a1a1a1aL);
+  rotated &= ~c;
+  rotated >>= 2;
+  rotated &= uint64_t(0x2020202020202020L);
+  c += rotated;
+}
+
+} // anon namespace
+
+void toLowerAscii(char* str, size_t length) {
+  static const size_t kAlignMask64 = 7;
+  static const size_t kAlignMask32 = 3;
+
+  // Convert a character at a time until we reach an address that
+  // is at least 32-bit aligned
+  size_t n = (size_t)str;
+  n &= kAlignMask32;
+  n = std::min(n, length);
+  size_t offset = 0;
+  if (n != 0) {
+    n = std::min(4 - n, length);
+    do {
+      toLowerAscii8(str[offset]);
+      offset++;
+    } while (offset < n);
+  }
+
+  n = (size_t)(str + offset);
+  n &= kAlignMask64;
+  if ((n != 0) && (offset + 4 <= length)) {
+    // The next address is 32-bit aligned but not 64-bit aligned.
+    // Convert the next 4 bytes in order to get to the 64-bit aligned
+    // part of the input.
+    toLowerAscii32(*(uint32_t*)(str + offset));
+    offset += 4;
+  }
+
+  // Convert 8 characters at a time
+  while (offset + 8 <= length) {
+    toLowerAscii64(*(uint64_t*)(str + offset));
+    offset += 8;
+  }
+
+  // Convert 4 characters at a time
+  while (offset + 4 <= length) {
+    toLowerAscii32(*(uint32_t*)(str + offset));
+    offset += 4;
+  }
+
+  // Convert any characters remaining after the last 4-byte aligned group
+  while (offset < length) {
+    toLowerAscii8(str[offset]);
+    offset++;
+  }
+}
+
+namespace detail {
+
+size_t hexDumpLine(const void* ptr, size_t offset, size_t size,
+                   std::string& line) {
+  // Line layout:
+  // 8: address
+  // 1: space
+  // (1+2)*16: hex bytes, each preceded by a space
+  // 1: space separating the two halves
+  // 3: "  |"
+  // 16: characters
+  // 1: "|"
+  // Total: 78
+  line.clear();
+  line.reserve(78);
+  const uint8_t* p = reinterpret_cast<const uint8_t*>(ptr) + offset;
+  size_t n = std::min(size - offset, size_t(16));
+  format("{:08x} ", offset).appendTo(line);
+
+  for (size_t i = 0; i < n; i++) {
+    if (i == 8) {
+      line.push_back(' ');
+    }
+    format(" {:02x}", p[i]).appendTo(line);
+  }
+
+  // 3 spaces for each byte we're not printing, one separating the halves
+  // if necessary
+  line.append(3 * (16 - n) + (n <= 8), ' ');
+  line.append("  |");
+
+  for (size_t i = 0; i < n; i++) {
+    char c = (p[i] >= 32 && p[i] <= 126 ? static_cast<char>(p[i]) : '.');
+    line.push_back(c);
+  }
+  line.append(16 - n, ' ');
+  line.push_back('|');
+  DCHECK_EQ(line.size(), 78);
+
+  return n;
+}
+
+} // namespace detail
+
+}   // namespace folly
+
+#ifdef FOLLY_DEFINED_DMGL
+# undef FOLLY_DEFINED_DMGL
+# undef DMGL_NO_OPTS
+# undef DMGL_PARAMS
+# undef DMGL_ANSI
+# undef DMGL_JAVA
+# undef DMGL_VERBOSE
+# undef DMGL_TYPES
+# undef DMGL_RET_POSTFIX
+#endif
diff --git a/faux-folly/folly/String.h b/faux-folly/folly/String.h
new file mode 100644
index 0000000..04004b5
--- /dev/null
+++ b/faux-folly/folly/String.h
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_BASE_STRING_H_
+#define FOLLY_BASE_STRING_H_
+
+#include <exception>
+#include <stdarg.h>
+#include <string>
+#include <boost/type_traits.hpp>
+
+#ifdef FOLLY_HAVE_DEPRECATED_ASSOC
+#ifdef _GLIBCXX_SYMVER
+#include <ext/hash_set>
+#include <ext/hash_map>
+#endif
+#endif
+
+#include <unordered_set>
+#include <unordered_map>
+
+#include <folly/Conv.h>
+#include <folly/Demangle.h>
+#include <folly/FBString.h>
+#include <folly/FBVector.h>
+#include <folly/Portability.h>
+#include <folly/Range.h>
+#include <folly/ScopeGuard.h>
+
+// Compatibility function, to make sure toStdString(s) can be called
+// to convert a std::string or fbstring variable s into type std::string
+// with very little overhead if s was already std::string
+namespace folly {
+
+inline
+std::string toStdString(const folly::fbstring& s) {
+  return std::string(s.data(), s.size());
+}
+
+inline
+const std::string& toStdString(const std::string& s) {
+  return s;
+}
+
+// If called with a temporary, the compiler will select this overload instead
+// of the above, so we don't return a (lvalue) reference to a temporary.
+inline
+std::string&& toStdString(std::string&& s) {
+  return std::move(s);
+}
+
+/**
+ * C-Escape a string, making it suitable for representation as a C string
+ * literal.  Appends the result to the output string.
+ *
+ * Backslashes all occurrences of backslash and double-quote:
+ *   "  ->  \"
+ *   \  ->  \\
+ *
+ * Replaces all non-printable ASCII characters with backslash-octal
+ * representation:
+ *   <ASCII 254> -> \376
+ *
+ * Note that we use backslash-octal instead of backslash-hex because the octal
+ * representation is guaranteed to consume no more than 3 characters; "\3760"
+ * represents two characters, one with value 254, and one with value 48 ('0'),
+ * whereas "\xfe0" represents only one character (with value 4064, which leads
+ * to implementation-defined behavior).
+ */
+template <class String>
+void cEscape(StringPiece str, String& out);
+
+/**
+ * Similar to cEscape above, but returns the escaped string.
+ */
+template <class String>
+String cEscape(StringPiece str) {
+  String out;
+  cEscape(str, out);
+  return out;
+}
+
+/**
+ * C-Unescape a string; the opposite of cEscape above.  Appends the result
+ * to the output string.
+ *
+ * Recognizes the standard C escape sequences:
+ *
+ * \' \" \? \\ \a \b \f \n \r \t \v
+ * \[0-7]+
+ * \x[0-9a-fA-F]+
+ *
+ * In strict mode (default), throws std::invalid_argument if it encounters
+ * an unrecognized escape sequence.  In non-strict mode, it leaves
+ * the escape sequence unchanged.
+ */
+template <class String>
+void cUnescape(StringPiece str, String& out, bool strict = true);
+
+/**
+ * Similar to cUnescape above, but returns the escaped string.
+ */
+template <class String>
+String cUnescape(StringPiece str, bool strict = true) {
+  String out;
+  cUnescape(str, out, strict);
+  return out;
+}
+
+/**
+ * URI-escape a string.  Appends the result to the output string.
+ *
+ * Alphanumeric characters and other characters marked as "unreserved" in RFC
+ * 3986 ( -_.~ ) are left unchanged.  In PATH mode, the forward slash (/) is
+ * also left unchanged.  In QUERY mode, spaces are replaced by '+'.  All other
+ * characters are percent-encoded.
+ */
+enum class UriEscapeMode : unsigned char {
+  // The values are meaningful, see generate_escape_tables.py
+  ALL = 0,
+  QUERY = 1,
+  PATH = 2
+};
+template <class String>
+void uriEscape(StringPiece str,
+               String& out,
+               UriEscapeMode mode = UriEscapeMode::ALL);
+
+/**
+ * Similar to uriEscape above, but returns the escaped string.
+ */
+template <class String>
+String uriEscape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) {
+  String out;
+  uriEscape(str, out, mode);
+  return out;
+}
+
+/**
+ * URI-unescape a string.  Appends the result to the output string.
+ *
+ * In QUERY mode, '+' are replaced by space.  %XX sequences are decoded if
+ * XX is a valid hex sequence, otherwise we throw invalid_argument.
+ */
+template <class String>
+void uriUnescape(StringPiece str,
+                 String& out,
+                 UriEscapeMode mode = UriEscapeMode::ALL);
+
+/**
+ * Similar to uriUnescape above, but returns the unescaped string.
+ */
+template <class String>
+String uriUnescape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) {
+  String out;
+  uriUnescape(str, out, mode);
+  return out;
+}
+
+/**
+ * stringPrintf is much like printf but deposits its result into a
+ * string. Two signatures are supported: the first simply returns the
+ * resulting string, and the second appends the produced characters to
+ * the specified string and returns a reference to it.
+ */
+std::string stringPrintf(FOLLY_PRINTF_FORMAT const char* format, ...)
+  FOLLY_PRINTF_FORMAT_ATTR(1, 2);
+
+/* Similar to stringPrintf, with different signature. */
+void stringPrintf(std::string* out, FOLLY_PRINTF_FORMAT const char* fmt, ...)
+  FOLLY_PRINTF_FORMAT_ATTR(2, 3);
+
+std::string& stringAppendf(std::string* output,
+                          FOLLY_PRINTF_FORMAT const char* format, ...)
+  FOLLY_PRINTF_FORMAT_ATTR(2, 3);
+
+/**
+ * Similar to stringPrintf, but accepts a va_list argument.
+ *
+ * As with vsnprintf() itself, the value of ap is undefined after the call.
+ * These functions do not call va_end() on ap.
+ */
+std::string stringVPrintf(const char* format, va_list ap);
+void stringVPrintf(std::string* out, const char* format, va_list ap);
+std::string& stringVAppendf(std::string* out, const char* format, va_list ap);
+
+/**
+ * Backslashify a string, that is, replace non-printable characters
+ * with C-style (but NOT C compliant) "\xHH" encoding.  If hex_style
+ * is false, then shorthand notations like "\0" will be used instead
+ * of "\x00" for the most common backslash cases.
+ *
+ * There are two forms, one returning the input string, and one
+ * creating output in the specified output string.
+ *
+ * This is mainly intended for printing to a terminal, so it is not
+ * particularly optimized.
+ *
+ * Do *not* use this in situations where you expect to be able to feed
+ * the string to a C or C++ compiler, as there are nuances with how C
+ * parses such strings that lead to failures.  This is for display
+ * purposed only.  If you want a string you can embed for use in C or
+ * C++, use cEscape instead.  This function is for display purposes
+ * only.
+ */
+template <class String1, class String2>
+void backslashify(const String1& input, String2& output, bool hex_style=false);
+
+template <class String>
+String backslashify(const String& input, bool hex_style=false) {
+  String output;
+  backslashify(input, output, hex_style);
+  return output;
+}
+
+/**
+ * Take a string and "humanify" it -- that is, make it look better.
+ * Since "better" is subjective, caveat emptor.  The basic approach is
+ * to count the number of unprintable characters.  If there are none,
+ * then the output is the input.  If there are relatively few, or if
+ * there is a long "enough" prefix of printable characters, use
+ * backslashify.  If it is mostly binary, then simply hex encode.
+ *
+ * This is an attempt to make a computer smart, and so likely is wrong
+ * most of the time.
+ */
+template <class String1, class String2>
+void humanify(const String1& input, String2& output);
+
+template <class String>
+String humanify(const String& input) {
+  String output;
+  humanify(input, output);
+  return output;
+}
+
+/**
+ * Same functionality as Python's binascii.hexlify.  Returns true
+ * on successful conversion.
+ *
+ * If append_output is true, append data to the output rather than
+ * replace it.
+ */
+template<class InputString, class OutputString>
+bool hexlify(const InputString& input, OutputString& output,
+             bool append=false);
+
+/**
+ * Same functionality as Python's binascii.unhexlify.  Returns true
+ * on successful conversion.
+ */
+template<class InputString, class OutputString>
+bool unhexlify(const InputString& input, OutputString& output);
+
+/*
+ * A pretty-printer for numbers that appends suffixes of units of the
+ * given type.  It prints 4 sig-figs of value with the most
+ * appropriate unit.
+ *
+ * If `addSpace' is true, we put a space between the units suffix and
+ * the value.
+ *
+ * Current types are:
+ *   PRETTY_TIME         - s, ms, us, ns, etc.
+ *   PRETTY_BYTES_METRIC - kB, MB, GB, etc (goes up by 10^3 = 1000 each time)
+ *   PRETTY_BYTES        - kB, MB, GB, etc (goes up by 2^10 = 1024 each time)
+ *   PRETTY_BYTES_IEC    - KiB, MiB, GiB, etc
+ *   PRETTY_UNITS_METRIC - k, M, G, etc (goes up by 10^3 = 1000 each time)
+ *   PRETTY_UNITS_BINARY - k, M, G, etc (goes up by 2^10 = 1024 each time)
+ *   PRETTY_UNITS_BINARY_IEC - Ki, Mi, Gi, etc
+ *   PRETTY_SI           - full SI metric prefixes from yocto to Yotta
+ *                         http://en.wikipedia.org/wiki/Metric_prefix
+ * @author Mark Rabkin <mrabkin@fb.com>
+ */
+enum PrettyType {
+  PRETTY_TIME,
+
+  PRETTY_BYTES_METRIC,
+  PRETTY_BYTES_BINARY,
+  PRETTY_BYTES = PRETTY_BYTES_BINARY,
+  PRETTY_BYTES_BINARY_IEC,
+  PRETTY_BYTES_IEC = PRETTY_BYTES_BINARY_IEC,
+
+  PRETTY_UNITS_METRIC,
+  PRETTY_UNITS_BINARY,
+  PRETTY_UNITS_BINARY_IEC,
+
+  PRETTY_SI,
+  PRETTY_NUM_TYPES,
+};
+
+std::string prettyPrint(double val, PrettyType, bool addSpace = true);
+
+/**
+ * This utility converts StringPiece in pretty format (look above) to double,
+ * with progress information. Alters the  StringPiece parameter
+ * to get rid of the already-parsed characters.
+ * Expects string in form <floating point number> {space}* [<suffix>]
+ * If string is not in correct format, utility finds longest valid prefix and
+ * if there at least one, returns double value based on that prefix and
+ * modifies string to what is left after parsing. Throws and std::range_error
+ * exception if there is no correct parse.
+ * Examples(for PRETTY_UNITS_METRIC):
+ * '10M' => 10 000 000
+ * '10 M' => 10 000 000
+ * '10' => 10
+ * '10 Mx' => 10 000 000, prettyString == "x"
+ * 'abc' => throws std::range_error
+ */
+double prettyToDouble(folly::StringPiece *const prettyString,
+                      const PrettyType type);
+
+/*
+ * Same as prettyToDouble(folly::StringPiece*, PrettyType), but
+ * expects whole string to be correctly parseable. Throws std::range_error
+ * otherwise
+ */
+double prettyToDouble(folly::StringPiece prettyString, const PrettyType type);
+
+/**
+ * Write a hex dump of size bytes starting at ptr to out.
+ *
+ * The hex dump is formatted as follows:
+ *
+ * for the string "abcdefghijklmnopqrstuvwxyz\x02"
+00000000  61 62 63 64 65 66 67 68  69 6a 6b 6c 6d 6e 6f 70  |abcdefghijklmnop|
+00000010  71 72 73 74 75 76 77 78  79 7a 02                 |qrstuvwxyz.     |
+ *
+ * that is, we write 16 bytes per line, both as hex bytes and as printable
+ * characters.  Non-printable characters are replaced with '.'
+ * Lines are written to out one by one (one StringPiece at a time) without
+ * delimiters.
+ */
+template <class OutIt>
+void hexDump(const void* ptr, size_t size, OutIt out);
+
+/**
+ * Return the hex dump of size bytes starting at ptr as a string.
+ */
+std::string hexDump(const void* ptr, size_t size);
+
+/**
+ * Return a fbstring containing the description of the given errno value.
+ * Takes care not to overwrite the actual system errno, so calling
+ * errnoStr(errno) is valid.
+ */
+fbstring errnoStr(int err);
+
+/**
+ * Debug string for an exception: include type and what(), if
+ * defined.
+ */
+inline fbstring exceptionStr(const std::exception& e) {
+#ifdef FOLLY_HAS_RTTI
+  return folly::to<fbstring>(demangle(typeid(e)), ": ", e.what());
+#else
+  return folly::to<fbstring>("Exception (no RTTI available): ", e.what());
+#endif
+}
+
+// Empirically, this indicates if the runtime supports
+// std::exception_ptr, as not all (arm, for instance) do.
+#if defined(__GNUC__) && defined(__GCC_ATOMIC_INT_LOCK_FREE) && \
+  __GCC_ATOMIC_INT_LOCK_FREE > 1
+inline fbstring exceptionStr(std::exception_ptr ep) {
+  try {
+    std::rethrow_exception(ep);
+  } catch (const std::exception& e) {
+    return exceptionStr(e);
+  } catch (...) {
+    return "<unknown exception>";
+  }
+}
+#endif
+
+template<typename E>
+auto exceptionStr(const E& e)
+  -> typename std::enable_if<!std::is_base_of<std::exception, E>::value,
+                             fbstring>::type
+{
+#ifdef FOLLY_HAS_RTTI
+  return folly::to<fbstring>(demangle(typeid(e)));
+#else
+  return "Exception (no RTTI available)";
+#endif
+}
+
+/*
+ * Split a string into a list of tokens by delimiter.
+ *
+ * The split interface here supports different output types, selected
+ * at compile time: StringPiece, fbstring, or std::string.  If you are
+ * using a vector to hold the output, it detects the type based on
+ * what your vector contains.  If the output vector is not empty, split
+ * will append to the end of the vector.
+ *
+ * You can also use splitTo() to write the output to an arbitrary
+ * OutputIterator (e.g. std::inserter() on a std::set<>), in which
+ * case you have to tell the function the type.  (Rationale:
+ * OutputIterators don't have a value_type, so we can't detect the
+ * type in splitTo without being told.)
+ *
+ * Examples:
+ *
+ *   std::vector<folly::StringPiece> v;
+ *   folly::split(":", "asd:bsd", v);
+ *
+ *   std::set<StringPiece> s;
+ *   folly::splitTo<StringPiece>(":", "asd:bsd:asd:csd",
+ *    std::inserter(s, s.begin()));
+ *
+ * Split also takes a flag (ignoreEmpty) that indicates whether adjacent
+ * delimiters should be treated as one single separator (ignoring empty tokens)
+ * or not (generating empty tokens).
+ */
+
+template<class Delim, class String, class OutputType>
+void split(const Delim& delimiter,
+           const String& input,
+           std::vector<OutputType>& out,
+           bool ignoreEmpty = false);
+
+template<class Delim, class String, class OutputType>
+void split(const Delim& delimiter,
+           const String& input,
+           folly::fbvector<OutputType>& out,
+           bool ignoreEmpty = false);
+
+template<class OutputValueType, class Delim, class String,
+         class OutputIterator>
+void splitTo(const Delim& delimiter,
+             const String& input,
+             OutputIterator out,
+             bool ignoreEmpty = false);
+
+/*
+ * Split a string into a fixed number of string pieces and/or numeric types
+ * by delimiter. Any numeric type that folly::to<> can convert to from a
+ * string piece is supported as a target. Returns 'true' if the fields were
+ * all successfully populated.  Returns 'false' if there were too few fields
+ * in the input, or too many fields if exact=true.  Casting exceptions will
+ * not be caught.
+ *
+ * Examples:
+ *
+ *  folly::StringPiece name, key, value;
+ *  if (folly::split('\t', line, name, key, value))
+ *    ...
+ *
+ *  folly::StringPiece name;
+ *  double value;
+ *  int id;
+ *  if (folly::split('\t', line, name, value, id))
+ *    ...
+ *
+ * The 'exact' template parameter specifies how the function behaves when too
+ * many fields are present in the input string. When 'exact' is set to its
+ * default value of 'true', a call to split will fail if the number of fields in
+ * the input string does not exactly match the number of output parameters
+ * passed. If 'exact' is overridden to 'false', all remaining fields will be
+ * stored, unsplit, in the last field, as shown below:
+ *
+ *  folly::StringPiece x, y.
+ *  if (folly::split<false>(':', "a:b:c", x, y))
+ *    assert(x == "a" && y == "b:c");
+ *
+ * Note that this will likely not work if the last field's target is of numeric
+ * type, in which case folly::to<> will throw an exception.
+ */
+template <class T>
+using IsSplitTargetType = std::integral_constant<bool,
+  std::is_arithmetic<T>::value ||
+  std::is_same<T, StringPiece>::value ||
+  IsSomeString<T>::value>;
+
+template<bool exact = true,
+         class Delim,
+         class OutputType,
+         class... OutputTypes>
+typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
+split(const Delim& delimiter,
+      StringPiece input,
+      OutputType& outHead,
+      OutputTypes&... outTail);
+
+/*
+ * Join list of tokens.
+ *
+ * Stores a string representation of tokens in the same order with
+ * deliminer between each element.
+ */
+
+template <class Delim, class Iterator, class String>
+void join(const Delim& delimiter,
+          Iterator begin,
+          Iterator end,
+          String& output);
+
+template <class Delim, class Container, class String>
+void join(const Delim& delimiter,
+          const Container& container,
+          String& output) {
+  join(delimiter, container.begin(), container.end(), output);
+}
+
+template <class Delim, class Value, class String>
+void join(const Delim& delimiter,
+          const std::initializer_list<Value>& values,
+          String& output) {
+  join(delimiter, values.begin(), values.end(), output);
+}
+
+template <class Delim, class Container>
+std::string join(const Delim& delimiter,
+                 const Container& container) {
+  std::string output;
+  join(delimiter, container.begin(), container.end(), output);
+  return output;
+}
+
+template <class Delim, class Value>
+std::string join(const Delim& delimiter,
+                 const std::initializer_list<Value>& values) {
+  std::string output;
+  join(delimiter, values.begin(), values.end(), output);
+  return output;
+}
+
+template <class Delim,
+          class Iterator,
+          typename std::enable_if<std::is_same<
+              typename std::iterator_traits<Iterator>::iterator_category,
+              std::random_access_iterator_tag>::value>::type* = nullptr>
+std::string join(const Delim& delimiter, Iterator begin, Iterator end) {
+  std::string output;
+  join(delimiter, begin, end, output);
+  return output;
+}
+
+/**
+ * Returns a subpiece with all whitespace removed from the front of @sp.
+ * Whitespace means any of [' ', '\n', '\r', '\t'].
+ */
+StringPiece ltrimWhitespace(StringPiece sp);
+
+/**
+ * Returns a subpiece with all whitespace removed from the back of @sp.
+ * Whitespace means any of [' ', '\n', '\r', '\t'].
+ */
+StringPiece rtrimWhitespace(StringPiece sp);
+
+/**
+ * Returns a subpiece with all whitespace removed from the back and front of @sp.
+ * Whitespace means any of [' ', '\n', '\r', '\t'].
+ */
+inline StringPiece trimWhitespace(StringPiece sp) {
+  return ltrimWhitespace(rtrimWhitespace(sp));
+}
+
+/**
+ * Returns a subpiece with all whitespace removed from the front of @sp.
+ * Whitespace means any of [' ', '\n', '\r', '\t'].
+ * DEPRECATED: @see ltrimWhitespace @see rtrimWhitespace
+ */
+inline StringPiece skipWhitespace(StringPiece sp) {
+  return ltrimWhitespace(sp);
+}
+
+/**
+ * Fast, in-place lowercasing of ASCII alphabetic characters in strings.
+ * Leaves all other characters unchanged, including those with the 0x80
+ * bit set.
+ * @param str String to convert
+ * @param len Length of str, in bytes
+ */
+void toLowerAscii(char* str, size_t length);
+
+inline void toLowerAscii(MutableStringPiece str) {
+  toLowerAscii(str.begin(), str.size());
+}
+
+} // namespace folly
+
+// Hook into boost's type traits
+namespace boost {
+template <class T>
+struct has_nothrow_constructor<folly::basic_fbstring<T> > : true_type {
+  enum { value = true };
+};
+} // namespace boost
+
+#include <folly/String-inl.h>
+
+#endif
diff --git a/faux-folly/folly/StringBase.cpp b/faux-folly/folly/StringBase.cpp
new file mode 100644
index 0000000..e51dea9
--- /dev/null
+++ b/faux-folly/folly/StringBase.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 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/String.h>
+
+namespace folly {
+
+static inline bool is_oddspace(char c) {
+  return c == '\n' || c == '\t' || c == '\r';
+}
+
+StringPiece ltrimWhitespace(StringPiece sp) {
+  // Spaces other than ' ' characters are less common but should be
+  // checked.  This configuration where we loop on the ' '
+  // separately from oddspaces was empirically fastest.
+
+loop:
+  for (; !sp.empty() && sp.front() == ' '; sp.pop_front()) {
+  }
+  if (!sp.empty() && is_oddspace(sp.front())) {
+    sp.pop_front();
+    goto loop;
+  }
+
+  return sp;
+}
+
+StringPiece rtrimWhitespace(StringPiece sp) {
+  // Spaces other than ' ' characters are less common but should be
+  // checked.  This configuration where we loop on the ' '
+  // separately from oddspaces was empirically fastest.
+
+loop:
+  for (; !sp.empty() && sp.back() == ' '; sp.pop_back()) {
+  }
+  if (!sp.empty() && is_oddspace(sp.back())) {
+    sp.pop_back();
+    goto loop;
+  }
+
+  return sp;
+}
+
+}
diff --git a/faux-folly/folly/ThreadCachedInt.h b/faux-folly/folly/ThreadCachedInt.h
new file mode 100644
index 0000000..8522a5a
--- /dev/null
+++ b/faux-folly/folly/ThreadCachedInt.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Higher performance (up to 10x) atomic increment using thread caching.
+ *
+ * @author Spencer Ahrens (sahrens)
+ */
+
+#ifndef FOLLY_THREADCACHEDINT_H
+#define FOLLY_THREADCACHEDINT_H
+
+#include <atomic>
+
+#include <boost/noncopyable.hpp>
+
+#include <folly/Likely.h>
+#include <folly/ThreadLocal.h>
+
+namespace folly {
+
+
+// Note that readFull requires holding a lock and iterating through all of the
+// thread local objects with the same Tag, so if you have a lot of
+// ThreadCachedInt's you should considering breaking up the Tag space even
+// further.
+template <class IntT, class Tag=IntT>
+class ThreadCachedInt : boost::noncopyable {
+  struct IntCache;
+
+ public:
+  explicit ThreadCachedInt(IntT initialVal = 0, uint32_t cacheSize = 1000)
+    : target_(initialVal), cacheSize_(cacheSize) {
+  }
+
+  void increment(IntT inc) {
+    auto cache = cache_.get();
+    if (UNLIKELY(cache == nullptr || cache->parent_ == nullptr)) {
+      cache = new IntCache(*this);
+      cache_.reset(cache);
+    }
+    cache->increment(inc);
+  }
+
+  // Quickly grabs the current value which may not include some cached
+  // increments.
+  IntT readFast() const {
+    return target_.load(std::memory_order_relaxed);
+  }
+
+  // Reads the current value plus all the cached increments.  Requires grabbing
+  // a lock, so this is significantly slower than readFast().
+  IntT readFull() const {
+    IntT ret = readFast();
+    for (const auto& cache : cache_.accessAllThreads()) {
+      if (!cache.reset_.load(std::memory_order_acquire)) {
+        ret += cache.val_.load(std::memory_order_relaxed);
+      }
+    }
+    return ret;
+  }
+
+  // Quickly reads and resets current value (doesn't reset cached increments).
+  IntT readFastAndReset() {
+    return target_.exchange(0, std::memory_order_release);
+  }
+
+  // This function is designed for accumulating into another counter, where you
+  // only want to count each increment once.  It can still get the count a
+  // little off, however, but it should be much better than calling readFull()
+  // and set(0) sequentially.
+  IntT readFullAndReset() {
+    IntT ret = readFastAndReset();
+    for (auto& cache : cache_.accessAllThreads()) {
+      if (!cache.reset_.load(std::memory_order_acquire)) {
+        ret += cache.val_.load(std::memory_order_relaxed);
+        cache.reset_.store(true, std::memory_order_release);
+      }
+    }
+    return ret;
+  }
+
+  void setCacheSize(uint32_t newSize) {
+    cacheSize_.store(newSize, std::memory_order_release);
+  }
+
+  uint32_t getCacheSize() const {
+    return cacheSize_.load();
+  }
+
+  ThreadCachedInt& operator+=(IntT inc) { increment(inc); return *this; }
+  ThreadCachedInt& operator-=(IntT inc) { increment(-inc); return *this; }
+  // pre-increment (we don't support post-increment)
+  ThreadCachedInt& operator++() { increment(1); return *this; }
+  ThreadCachedInt& operator--() { increment(-1); return *this; }
+
+  // Thread-safe set function.
+  // This is a best effort implementation. In some edge cases, there could be
+  // data loss (missing counts)
+  void set(IntT newVal) {
+    for (auto& cache : cache_.accessAllThreads()) {
+      cache.reset_.store(true, std::memory_order_release);
+    }
+    target_.store(newVal, std::memory_order_release);
+  }
+
+  // This is a little tricky - it's possible that our IntCaches are still alive
+  // in another thread and will get destroyed after this destructor runs, so we
+  // need to make sure we signal that this parent is dead.
+  ~ThreadCachedInt() {
+    for (auto& cache : cache_.accessAllThreads()) {
+      cache.parent_ = nullptr;
+    }
+  }
+
+ private:
+  std::atomic<IntT> target_;
+  std::atomic<uint32_t> cacheSize_;
+  ThreadLocalPtr<IntCache,Tag> cache_; // Must be last for dtor ordering
+
+  // This should only ever be modified by one thread
+  struct IntCache {
+    ThreadCachedInt* parent_;
+    mutable std::atomic<IntT> val_;
+    mutable uint32_t numUpdates_;
+    std::atomic<bool> reset_;
+
+    explicit IntCache(ThreadCachedInt& parent)
+        : parent_(&parent), val_(0), numUpdates_(0), reset_(false) {}
+
+    void increment(IntT inc) {
+      if (LIKELY(!reset_.load(std::memory_order_acquire))) {
+        // This thread is the only writer to val_, so it's fine do do
+        // a relaxed load and do the addition non-atomically.
+        val_.store(
+          val_.load(std::memory_order_relaxed) + inc,
+          std::memory_order_release
+        );
+      } else {
+        val_.store(inc, std::memory_order_relaxed);
+        reset_.store(false, std::memory_order_release);
+      }
+      ++numUpdates_;
+      if (UNLIKELY(numUpdates_ >
+                   parent_->cacheSize_.load(std::memory_order_acquire))) {
+        flush();
+      }
+    }
+
+    void flush() const {
+      parent_->target_.fetch_add(val_, std::memory_order_release);
+      val_.store(0, std::memory_order_release);
+      numUpdates_ = 0;
+    }
+
+    ~IntCache() {
+      if (parent_) {
+        flush();
+      }
+    }
+  };
+};
+
+}
+
+#endif
diff --git a/faux-folly/folly/ThreadLocal.h b/faux-folly/folly/ThreadLocal.h
new file mode 100644
index 0000000..fee6fac
--- /dev/null
+++ b/faux-folly/folly/ThreadLocal.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Improved thread local storage for non-trivial types (similar speed as
+ * pthread_getspecific but only consumes a single pthread_key_t, and 4x faster
+ * than boost::thread_specific_ptr).
+ *
+ * Also includes an accessor interface to walk all the thread local child
+ * objects of a parent.  accessAllThreads() initializes an accessor which holds
+ * a global lock *that blocks all creation and destruction of ThreadLocal
+ * objects with the same Tag* and can be used as an iterable container.
+ *
+ * Intended use is for frequent write, infrequent read data access patterns such
+ * as counters.
+ *
+ * There are two classes here - ThreadLocal and ThreadLocalPtr.  ThreadLocalPtr
+ * has semantics similar to boost::thread_specific_ptr. ThreadLocal is a thin
+ * wrapper around ThreadLocalPtr that manages allocation automatically.
+ *
+ * @author Spencer Ahrens (sahrens)
+ */
+
+#ifndef FOLLY_THREADLOCAL_H_
+#define FOLLY_THREADLOCAL_H_
+
+#include <folly/Portability.h>
+#include <boost/iterator/iterator_facade.hpp>
+#include <folly/Likely.h>
+#include <type_traits>
+
+
+namespace folly {
+enum class TLPDestructionMode {
+  THIS_THREAD,
+  ALL_THREADS
+};
+}  // namespace
+
+#include <folly/detail/ThreadLocalDetail.h>
+
+namespace folly {
+
+template<class T, class Tag> class ThreadLocalPtr;
+
+template<class T, class Tag=void>
+class ThreadLocal {
+ public:
+  ThreadLocal() = default;
+
+  T* get() const {
+    T* ptr = tlp_.get();
+    if (LIKELY(ptr != nullptr)) {
+      return ptr;
+    }
+
+    // separated new item creation out to speed up the fast path.
+    return makeTlp();
+  }
+
+  T* operator->() const {
+    return get();
+  }
+
+  T& operator*() const {
+    return *get();
+  }
+
+  void reset(T* newPtr = nullptr) {
+    tlp_.reset(newPtr);
+  }
+
+  typedef typename ThreadLocalPtr<T,Tag>::Accessor Accessor;
+  Accessor accessAllThreads() const {
+    return tlp_.accessAllThreads();
+  }
+
+  // movable
+  ThreadLocal(ThreadLocal&&) = default;
+  ThreadLocal& operator=(ThreadLocal&&) = default;
+
+ private:
+  // non-copyable
+  ThreadLocal(const ThreadLocal&) = delete;
+  ThreadLocal& operator=(const ThreadLocal&) = delete;
+
+  T* makeTlp() const {
+    T* ptr = new T();
+    tlp_.reset(ptr);
+    return ptr;
+  }
+
+  mutable ThreadLocalPtr<T,Tag> tlp_;
+};
+
+/*
+ * The idea here is that __thread is faster than pthread_getspecific, so we
+ * keep a __thread array of pointers to objects (ThreadEntry::elements) where
+ * each array has an index for each unique instance of the ThreadLocalPtr
+ * object.  Each ThreadLocalPtr object has a unique id that is an index into
+ * these arrays so we can fetch the correct object from thread local storage
+ * very efficiently.
+ *
+ * In order to prevent unbounded growth of the id space and thus huge
+ * ThreadEntry::elements, arrays, for example due to continuous creation and
+ * destruction of ThreadLocalPtr objects, we keep a set of all active
+ * instances.  When an instance is destroyed we remove it from the active
+ * set and insert the id into freeIds_ for reuse.  These operations require a
+ * global mutex, but only happen at construction and destruction time.
+ *
+ * We use a single global pthread_key_t per Tag to manage object destruction and
+ * memory cleanup upon thread exit because there is a finite number of
+ * pthread_key_t's available per machine.
+ *
+ * NOTE: Apple platforms don't support the same semantics for __thread that
+ *       Linux does (and it's only supported at all on i386). For these, use
+ *       pthread_setspecific()/pthread_getspecific() for the per-thread
+ *       storage.  Windows (MSVC and GCC) does support the same semantics
+ *       with __declspec(thread)
+ */
+
+template<class T, class Tag=void>
+class ThreadLocalPtr {
+ public:
+  ThreadLocalPtr() : id_(threadlocal_detail::StaticMeta<Tag>::create()) { }
+
+  ThreadLocalPtr(ThreadLocalPtr&& other) noexcept : id_(other.id_) {
+    other.id_ = 0;
+  }
+
+  ThreadLocalPtr& operator=(ThreadLocalPtr&& other) {
+    assert(this != &other);
+    destroy();
+    id_ = other.id_;
+    other.id_ = 0;
+    return *this;
+  }
+
+  ~ThreadLocalPtr() {
+    destroy();
+  }
+
+  T* get() const {
+    return static_cast<T*>(threadlocal_detail::StaticMeta<Tag>::get(id_).ptr);
+  }
+
+  T* operator->() const {
+    return get();
+  }
+
+  T& operator*() const {
+    return *get();
+  }
+
+  T* release() {
+    threadlocal_detail::ElementWrapper& w =
+      threadlocal_detail::StaticMeta<Tag>::get(id_);
+
+    return static_cast<T*>(w.release());
+  }
+
+  void reset(T* newPtr = nullptr) {
+    threadlocal_detail::ElementWrapper& w =
+      threadlocal_detail::StaticMeta<Tag>::get(id_);
+    if (w.ptr != newPtr) {
+      w.dispose(TLPDestructionMode::THIS_THREAD);
+      w.set(newPtr);
+    }
+  }
+
+  explicit operator bool() const {
+    return get() != nullptr;
+  }
+
+  /**
+   * reset() with a custom deleter:
+   * deleter(T* ptr, TLPDestructionMode mode)
+   * "mode" is ALL_THREADS if we're destructing this ThreadLocalPtr (and thus
+   * deleting pointers for all threads), and THIS_THREAD if we're only deleting
+   * the member for one thread (because of thread exit or reset())
+   */
+  template <class Deleter>
+  void reset(T* newPtr, Deleter deleter) {
+    threadlocal_detail::ElementWrapper& w =
+      threadlocal_detail::StaticMeta<Tag>::get(id_);
+    if (w.ptr != newPtr) {
+      w.dispose(TLPDestructionMode::THIS_THREAD);
+      w.set(newPtr, deleter);
+    }
+  }
+
+  // Holds a global lock for iteration through all thread local child objects.
+  // Can be used as an iterable container.
+  // Use accessAllThreads() to obtain one.
+  class Accessor {
+    friend class ThreadLocalPtr<T,Tag>;
+
+    threadlocal_detail::StaticMeta<Tag>& meta_;
+    std::mutex* lock_;
+    uint32_t id_;
+
+   public:
+    class Iterator;
+    friend class Iterator;
+
+    // The iterators obtained from Accessor are bidirectional iterators.
+    class Iterator : public boost::iterator_facade<
+          Iterator,                               // Derived
+          T,                                      // value_type
+          boost::bidirectional_traversal_tag> {   // traversal
+      friend class Accessor;
+      friend class boost::iterator_core_access;
+      const Accessor* const accessor_;
+      threadlocal_detail::ThreadEntry* e_;
+
+      void increment() {
+        e_ = e_->next;
+        incrementToValid();
+      }
+
+      void decrement() {
+        e_ = e_->prev;
+        decrementToValid();
+      }
+
+      T& dereference() const {
+        return *static_cast<T*>(e_->elements[accessor_->id_].ptr);
+      }
+
+      bool equal(const Iterator& other) const {
+        return (accessor_->id_ == other.accessor_->id_ &&
+                e_ == other.e_);
+      }
+
+      explicit Iterator(const Accessor* accessor)
+        : accessor_(accessor),
+          e_(&accessor_->meta_.head_) {
+      }
+
+      bool valid() const {
+        return (e_->elements &&
+                accessor_->id_ < e_->elementsCapacity &&
+                e_->elements[accessor_->id_].ptr);
+      }
+
+      void incrementToValid() {
+        for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->next) { }
+      }
+
+      void decrementToValid() {
+        for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->prev) { }
+      }
+    };
+
+    ~Accessor() {
+      release();
+    }
+
+    Iterator begin() const {
+      return ++Iterator(this);
+    }
+
+    Iterator end() const {
+      return Iterator(this);
+    }
+
+    Accessor(const Accessor&) = delete;
+    Accessor& operator=(const Accessor&) = delete;
+
+    Accessor(Accessor&& other) noexcept
+      : meta_(other.meta_),
+        lock_(other.lock_),
+        id_(other.id_) {
+      other.id_ = 0;
+      other.lock_ = nullptr;
+    }
+
+    Accessor& operator=(Accessor&& other) noexcept {
+      // Each Tag has its own unique meta, and accessors with different Tags
+      // have different types.  So either *this is empty, or this and other
+      // have the same tag.  But if they have the same tag, they have the same
+      // meta (and lock), so they'd both hold the lock at the same time,
+      // which is impossible, which leaves only one possible scenario --
+      // *this is empty.  Assert it.
+      assert(&meta_ == &other.meta_);
+      assert(lock_ == nullptr);
+      using std::swap;
+      swap(lock_, other.lock_);
+      swap(id_, other.id_);
+    }
+
+    Accessor()
+      : meta_(threadlocal_detail::StaticMeta<Tag>::instance()),
+        lock_(nullptr),
+        id_(0) {
+    }
+
+   private:
+    explicit Accessor(uint32_t id)
+      : meta_(threadlocal_detail::StaticMeta<Tag>::instance()),
+        lock_(&meta_.lock_) {
+      lock_->lock();
+      id_ = id;
+    }
+
+    void release() {
+      if (lock_) {
+        lock_->unlock();
+        id_ = 0;
+        lock_ = nullptr;
+      }
+    }
+  };
+
+  // accessor allows a client to iterate through all thread local child
+  // elements of this ThreadLocal instance.  Holds a global lock for each <Tag>
+  Accessor accessAllThreads() const {
+    static_assert(!std::is_same<Tag, void>::value,
+                  "Must use a unique Tag to use the accessAllThreads feature");
+    return Accessor(id_);
+  }
+
+ private:
+  void destroy() {
+    if (id_) {
+      threadlocal_detail::StaticMeta<Tag>::destroy(id_);
+    }
+  }
+
+  // non-copyable
+  ThreadLocalPtr(const ThreadLocalPtr&) = delete;
+  ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete;
+
+  uint32_t id_;  // every instantiation has a unique id
+};
+
+}  // namespace folly
+
+#endif /* FOLLY_THREADLOCAL_H_ */
diff --git a/faux-folly/folly/TokenBucket.h b/faux-folly/folly/TokenBucket.h
new file mode 100644
index 0000000..f5d0459
--- /dev/null
+++ b/faux-folly/folly/TokenBucket.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <limits>
+#include <chrono>
+
+#include <folly/Likely.h>
+
+namespace folly {
+
+/** Threadsafe TokenBucket implementation, based on the idea of
+ * converting tokens into time and maintaining state as a timestamp relative to
+ * now.  The number of tokens available is represented by the delta between now
+ * and the timestamp, and the 'burst' is represented by the maximum delta.
+ */
+class TokenBucket {
+ private:
+  std::atomic<double> time_;
+  std::atomic<double> secondsPerToken_;
+  std::atomic<double> secondsPerBurst_;
+
+ public:
+  TokenBucket(double rate, double burst, double nowInSeconds) noexcept
+      : time_(nowInSeconds) {
+    reset(rate, burst, nowInSeconds);
+  }
+
+  void reset(double rate, double burst, double nowInSeconds) noexcept {
+    double tokens = available(nowInSeconds);
+
+    secondsPerToken_.store(
+        1.0 / rate - std::numeric_limits<double>::epsilon(),
+        std::memory_order_relaxed);
+
+    secondsPerBurst_.store(
+        burst / rate + std::numeric_limits<double>::epsilon(),
+        std::memory_order_relaxed);
+
+    set_capacity(tokens, nowInSeconds);
+  }
+
+  void set_capacity(double tokens, double nowInSeconds) noexcept {
+    const double secondsPerToken = std::atomic_load_explicit(
+        &secondsPerToken_, std::memory_order_relaxed);
+
+    const double secondsPerBurst = std::atomic_load_explicit(
+        &secondsPerBurst_, std::memory_order_relaxed);
+
+    double newTime = nowInSeconds - std::min(
+        tokens * secondsPerToken, secondsPerBurst);
+
+    time_.store(newTime, std::memory_order_relaxed);
+  }
+
+  // If there are `tokens` avilable at `nowInSeconds`, consume them and
+  // return true.  Otherwise, return false.
+  //
+  // This implementation is written in a lock-free manner using a
+  // compare-and-exchange loop, with branch prediction optimized to minimize
+  // time spent in the 'success' case which performs a write.
+  bool consume(double tokens, double nowInSeconds) noexcept {
+    const double secondsNeeded = tokens * std::atomic_load_explicit(
+        &secondsPerToken_, std::memory_order_relaxed);
+
+    const double minTime = nowInSeconds - std::atomic_load_explicit(
+        &secondsPerBurst_, std::memory_order_relaxed);
+
+    double oldTime =
+      std::atomic_load_explicit(&time_, std::memory_order_relaxed);
+    double newTime = oldTime;
+
+    // Limit the number of available tokens to 'burst'.  We don't need to do
+    // this inside the loop because if we iterate more than once another
+    // caller will have performed an update that also covered this
+    // calculation.  Also, tell the compiler to optimize branch prediction to
+    // minimize time spent between reads and writes in the success case
+    if (UNLIKELY(minTime > oldTime)) {
+      newTime = minTime;
+    }
+
+    while (true) {
+      newTime += secondsNeeded;
+
+      // Optimize for the write-contention case, to minimize the impact of
+      // branch misprediction on other threads
+      if (UNLIKELY(newTime > nowInSeconds)) {
+        return false;
+      }
+
+      // Optimize for the write-contention case, to minimize the impact of
+      // branch misprediction on other threads
+      if (LIKELY(std::atomic_compare_exchange_weak_explicit(
+              &time_, &oldTime, newTime,
+              std::memory_order_relaxed, std::memory_order_relaxed))) {
+        return true;
+      }
+
+      newTime = oldTime;
+    }
+
+    return true;
+  }
+
+  // Similar to consume, but will always consume some number of tokens.
+  double consumeOrDrain(double tokens, double nowInSeconds) noexcept {
+    const double secondsPerToken = std::atomic_load_explicit(
+        &secondsPerToken_, std::memory_order_relaxed);
+
+    const double secondsNeeded = tokens * secondsPerToken;
+    const double minTime = nowInSeconds - std::atomic_load_explicit(
+        &secondsPerBurst_, std::memory_order_relaxed);
+
+    double oldTime =
+      std::atomic_load_explicit(&time_, std::memory_order_relaxed);
+    double newTime = oldTime;
+
+
+    // Limit the number of available tokens to 'burst'.
+    // Also, tell the compiler to optimize branch prediction to
+    // minimize time spent between reads and writes in the success case
+    if (UNLIKELY(minTime > oldTime)) {
+      newTime = minTime;
+    }
+
+    double consumed;
+
+    newTime += secondsNeeded;
+
+    consumed = (newTime - nowInSeconds) / secondsPerToken;
+    time_.store(newTime, std::memory_order_relaxed);
+
+    return consumed;
+  }
+
+  double available(double nowInSeconds = defaultClockNow()) const noexcept {
+    double time =
+      std::atomic_load_explicit(&time_, std::memory_order_relaxed);
+
+    double deltaTime = std::min(
+        std::atomic_load_explicit(&secondsPerBurst_,
+                                  std::memory_order_relaxed),
+        nowInSeconds - time);
+
+    return std::max(0.0, deltaTime / std::atomic_load_explicit(
+          &secondsPerToken_, std::memory_order_relaxed));
+  }
+
+  static double defaultClockNow() {
+    return std::chrono::duration_cast<std::chrono::microseconds>(
+        std::chrono::steady_clock::now().time_since_epoch()
+      ).count() / 1000000.0;
+  }
+};
+
+}
diff --git a/faux-folly/folly/Traits.h b/faux-folly/folly/Traits.h
new file mode 100644
index 0000000..0a29102
--- /dev/null
+++ b/faux-folly/folly/Traits.h
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author: Andrei Alexandrescu
+
+#ifndef FOLLY_BASE_TRAITS_H_
+#define FOLLY_BASE_TRAITS_H_
+
+#include <memory>
+#include <limits>
+#include <type_traits>
+#include <functional>
+
+#include <folly/Portability.h>
+
+// libc++ doesn't provide this header, nor does msvc
+#ifdef FOLLY_HAVE_BITS_CXXCONFIG_H
+// This file appears in two locations: inside fbcode and in the
+// libstdc++ source code (when embedding fbstring as std::string).
+// To aid in this schizophrenic use, two macros are defined in
+// c++config.h:
+//   _LIBSTDCXX_FBSTRING - Set inside libstdc++.  This is useful to
+//      gate use inside fbcode v. libstdc++
+#include <bits/c++config.h>
+#endif
+
+#include <boost/type_traits.hpp>
+#include <boost/mpl/and.hpp>
+#include <boost/mpl/has_xxx.hpp>
+#include <boost/mpl/not.hpp>
+
+namespace folly {
+
+/**
+ * IsRelocatable<T>::value describes the ability of moving around
+ * memory a value of type T by using memcpy (as opposed to the
+ * conservative approach of calling the copy constructor and then
+ * destroying the old temporary. Essentially for a relocatable type,
+ * the following two sequences of code should be semantically
+ * equivalent:
+ *
+ * void move1(T * from, T * to) {
+ *   new(to) T(from);
+ *   (*from).~T();
+ * }
+ *
+ * void move2(T * from, T * to) {
+ *   memcpy(to, from, sizeof(T));
+ * }
+ *
+ * Most C++ types are relocatable; the ones that aren't would include
+ * internal pointers or (very rarely) would need to update remote
+ * pointers to pointers tracking them. All C++ primitive types and
+ * type constructors are relocatable.
+ *
+ * This property can be used in a variety of optimizations. Currently
+ * fbvector uses this property intensively.
+ *
+ * The default conservatively assumes the type is not
+ * relocatable. Several specializations are defined for known
+ * types. You may want to add your own specializations. Do so in
+ * namespace folly and make sure you keep the specialization of
+ * IsRelocatable<SomeStruct> in the same header as SomeStruct.
+ *
+ * You may also declare a type to be relocatable by including
+ *    `typedef std::true_type IsRelocatable;`
+ * in the class header.
+ *
+ * It may be unset in a base class by overriding the typedef to false_type.
+ */
+/*
+ * IsTriviallyCopyable describes the value semantics property. C++11 contains
+ * the type trait is_trivially_copyable; however, it is not yet implemented
+ * in gcc (as of 4.7.1), and the user may wish to specify otherwise.
+ */
+/*
+ * IsZeroInitializable describes the property that default construction is the
+ * same as memset(dst, 0, sizeof(T)).
+ */
+
+namespace traits_detail {
+
+#define FOLLY_HAS_TRUE_XXX(name)                          \
+  BOOST_MPL_HAS_XXX_TRAIT_DEF(name);                      \
+  template <class T> struct name ## _is_true              \
+    : std::is_same<typename T::name, std::true_type> {};  \
+  template <class T> struct has_true_ ## name             \
+    : std::conditional<                                   \
+        has_ ## name <T>::value,                          \
+        name ## _is_true<T>,                              \
+        std::false_type                                   \
+      >:: type {};
+
+FOLLY_HAS_TRUE_XXX(IsRelocatable)
+FOLLY_HAS_TRUE_XXX(IsZeroInitializable)
+FOLLY_HAS_TRUE_XXX(IsTriviallyCopyable)
+
+#undef FOLLY_HAS_TRUE_XXX
+}
+
+template <class T> struct IsTriviallyCopyable
+  : std::integral_constant<bool,
+      !std::is_class<T>::value ||
+      // TODO: add alternate clause is_trivially_copyable, when available
+      traits_detail::has_true_IsTriviallyCopyable<T>::value
+    > {};
+
+template <class T> struct IsRelocatable
+  : std::integral_constant<bool,
+      !std::is_class<T>::value ||
+      // TODO add this line (and some tests for it) when we upgrade to gcc 4.7
+      //std::is_trivially_move_constructible<T>::value ||
+      IsTriviallyCopyable<T>::value ||
+      traits_detail::has_true_IsRelocatable<T>::value
+    > {};
+
+template <class T> struct IsZeroInitializable
+  : std::integral_constant<bool,
+      !std::is_class<T>::value ||
+      traits_detail::has_true_IsZeroInitializable<T>::value
+    > {};
+
+} // namespace folly
+
+/**
+ * Use this macro ONLY inside namespace folly. When using it with a
+ * regular type, use it like this:
+ *
+ * // Make sure you're at namespace ::folly scope
+ * template<> FOLLY_ASSUME_RELOCATABLE(MyType)
+ *
+ * When using it with a template type, use it like this:
+ *
+ * // Make sure you're at namespace ::folly scope
+ * template<class T1, class T2>
+ * FOLLY_ASSUME_RELOCATABLE(MyType<T1, T2>)
+ */
+#define FOLLY_ASSUME_RELOCATABLE(...) \
+  struct IsRelocatable<  __VA_ARGS__ > : std::true_type {};
+
+/**
+ * Use this macro ONLY inside namespace boost. When using it with a
+ * regular type, use it like this:
+ *
+ * // Make sure you're at namespace ::boost scope
+ * template<> FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MyType)
+ *
+ * When using it with a template type, use it like this:
+ *
+ * // Make sure you're at namespace ::boost scope
+ * template<class T1, class T2>
+ * FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MyType<T1, T2>)
+ */
+#define FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(...) \
+  struct has_nothrow_constructor<  __VA_ARGS__ > : ::boost::true_type {};
+
+/**
+ * The FOLLY_ASSUME_FBVECTOR_COMPATIBLE* macros below encode two
+ * assumptions: first, that the type is relocatable per IsRelocatable
+ * above, and that it has a nothrow constructor. Most types can be
+ * assumed to satisfy both conditions, but it is the responsibility of
+ * the user to state that assumption. User-defined classes will not
+ * work with fbvector (see FBVector.h) unless they state this
+ * combination of properties.
+ *
+ * Use FOLLY_ASSUME_FBVECTOR_COMPATIBLE with regular types like this:
+ *
+ * FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MyType)
+ *
+ * The versions FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1, _2, _3, and _4
+ * allow using the macro for describing templatized classes with 1, 2,
+ * 3, and 4 template parameters respectively. For template classes
+ * just use the macro with the appropriate number and pass the name of
+ * the template to it. Example:
+ *
+ * template <class T1, class T2> class MyType { ... };
+ * ...
+ * // Make sure you're at global scope
+ * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyType)
+ */
+
+// Use this macro ONLY at global level (no namespace)
+#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE(...)                           \
+  namespace folly { template<> FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__) }   \
+  namespace boost { \
+  template<> FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__) }
+// Use this macro ONLY at global level (no namespace)
+#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(...)                         \
+  namespace folly {                                                     \
+  template <class T1> FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1>) }       \
+    namespace boost {                                                   \
+    template <class T1> FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1>) }
+// Use this macro ONLY at global level (no namespace)
+#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(...)                 \
+  namespace folly {                                             \
+  template <class T1, class T2>                                 \
+  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2>) }               \
+    namespace boost {                                           \
+    template <class T1, class T2>                               \
+    FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1, T2>) }
+// Use this macro ONLY at global level (no namespace)
+#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(...)                         \
+  namespace folly {                                                     \
+  template <class T1, class T2, class T3>                               \
+  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3>) }                   \
+    namespace boost {                                                   \
+    template <class T1, class T2, class T3>                             \
+    FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1, T2, T3>) }
+// Use this macro ONLY at global level (no namespace)
+#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(...)                         \
+  namespace folly {                                                     \
+  template <class T1, class T2, class T3, class T4>                     \
+  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3, T4>) }               \
+    namespace boost {                                                   \
+    template <class T1, class T2, class T3, class T4>                   \
+    FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__<T1, T2, T3, T4>) }
+
+/**
+ * Instantiate FOLLY_ASSUME_FBVECTOR_COMPATIBLE for a few types. It is
+ * safe to assume that pair is compatible if both of its components
+ * are. Furthermore, all STL containers can be assumed to comply,
+ * although that is not guaranteed by the standard.
+ */
+
+FOLLY_NAMESPACE_STD_BEGIN
+
+template <class T, class U>
+  struct pair;
+#ifndef _GLIBCXX_USE_FB
+template <class T, class R, class A>
+  class basic_string;
+#else
+template <class T, class R, class A, class S>
+  class basic_string;
+#endif
+template <class T, class A>
+  class vector;
+template <class T, class A>
+  class deque;
+template <class T, class A>
+  class list;
+template <class T, class C, class A>
+  class set;
+template <class K, class V, class C, class A>
+  class map;
+template <class T>
+  class shared_ptr;
+
+FOLLY_NAMESPACE_STD_END
+
+namespace boost {
+
+template <class T> class shared_ptr;
+
+template <class T, class U>
+struct has_nothrow_constructor< std::pair<T, U> >
+    : ::boost::mpl::and_< has_nothrow_constructor<T>,
+                          has_nothrow_constructor<U> > {};
+
+} // namespace boost
+
+namespace folly {
+
+// STL commonly-used types
+template <class T, class U>
+struct IsRelocatable<  std::pair<T, U> >
+    : ::boost::mpl::and_< IsRelocatable<T>, IsRelocatable<U> > {};
+
+// Is T one of T1, T2, ..., Tn?
+template <class T, class... Ts>
+struct IsOneOf {
+  enum { value = false };
+};
+
+template <class T, class T1, class... Ts>
+struct IsOneOf<T, T1, Ts...> {
+  enum { value = std::is_same<T, T1>::value || IsOneOf<T, Ts...>::value };
+};
+
+/*
+ * Complementary type traits for integral comparisons.
+ *
+ * For instance, `if(x < 0)` yields an error in clang for unsigned types
+ *  when -Werror is used due to -Wtautological-compare
+ *
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+
+namespace detail {
+
+template <typename T, bool>
+struct is_negative_impl {
+  constexpr static bool check(T x) { return x < 0; }
+};
+
+template <typename T>
+struct is_negative_impl<T, false> {
+  constexpr static bool check(T) { return false; }
+};
+
+// folly::to integral specializations can end up generating code
+// inside what are really static ifs (not executed because of the templated
+// types) that violate -Wsign-compare so suppress them in order to not prevent
+// all calling code from using it.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-compare"
+
+template <typename RHS, RHS rhs, typename LHS>
+bool less_than_impl(
+  typename std::enable_if<
+    (rhs <= std::numeric_limits<LHS>::max()
+      && rhs > std::numeric_limits<LHS>::min()),
+    LHS
+  >::type const lhs
+) {
+  return lhs < rhs;
+}
+
+template <typename RHS, RHS rhs, typename LHS>
+bool less_than_impl(
+  typename std::enable_if<
+    (rhs > std::numeric_limits<LHS>::max()),
+    LHS
+  >::type const
+) {
+  return true;
+}
+
+template <typename RHS, RHS rhs, typename LHS>
+bool less_than_impl(
+  typename std::enable_if<
+    (rhs <= std::numeric_limits<LHS>::min()),
+    LHS
+  >::type const
+) {
+  return false;
+}
+
+#pragma GCC diagnostic pop
+
+template <typename RHS, RHS rhs, typename LHS>
+bool greater_than_impl(
+  typename std::enable_if<
+    (rhs <= std::numeric_limits<LHS>::max()
+      && rhs >= std::numeric_limits<LHS>::min()),
+    LHS
+  >::type const lhs
+) {
+  return lhs > rhs;
+}
+
+template <typename RHS, RHS rhs, typename LHS>
+bool greater_than_impl(
+  typename std::enable_if<
+    (rhs > std::numeric_limits<LHS>::max()),
+    LHS
+  >::type const
+) {
+  return false;
+}
+
+template <typename RHS, RHS rhs, typename LHS>
+bool greater_than_impl(
+  typename std::enable_if<
+    (rhs < std::numeric_limits<LHS>::min()),
+    LHS
+  >::type const
+) {
+  return true;
+}
+
+} // namespace detail {
+
+// same as `x < 0`
+template <typename T>
+constexpr bool is_negative(T x) {
+  return folly::detail::is_negative_impl<T, std::is_signed<T>::value>::check(x);
+}
+
+// same as `x <= 0`
+template <typename T>
+constexpr bool is_non_positive(T x) { return !x || folly::is_negative(x); }
+
+// same as `x > 0`
+template <typename T>
+constexpr bool is_positive(T x) { return !is_non_positive(x); }
+
+// same as `x >= 0`
+template <typename T>
+constexpr bool is_non_negative(T x) {
+  return !x || is_positive(x);
+}
+
+template <typename RHS, RHS rhs, typename LHS>
+bool less_than(LHS const lhs) {
+  return detail::less_than_impl<
+    RHS, rhs, typename std::remove_reference<LHS>::type
+  >(lhs);
+}
+
+template <typename RHS, RHS rhs, typename LHS>
+bool greater_than(LHS const lhs) {
+  return detail::greater_than_impl<
+    RHS, rhs, typename std::remove_reference<LHS>::type
+  >(lhs);
+}
+
+} // namespace folly
+
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(std::basic_string);
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::vector);
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::list);
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::deque);
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::unique_ptr);
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::shared_ptr);
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::function);
+
+// Boost
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(boost::shared_ptr);
+
+#define FOLLY_CREATE_HAS_MEMBER_TYPE_TRAITS(classname, type_name) \
+  template <typename T> \
+  struct classname { \
+    template <typename C> \
+    constexpr static bool test(typename C::type_name*) { return true; } \
+    template <typename> \
+    constexpr static bool test(...) { return false; } \
+    constexpr static bool value = test<T>(nullptr); \
+  }
+
+#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, cv_qual) \
+  template <typename TTheClass_, typename RTheReturn_, typename... TTheArgs_> \
+  class classname<TTheClass_, RTheReturn_(TTheArgs_...) cv_qual> { \
+    template < \
+      typename UTheClass_, RTheReturn_ (UTheClass_::*)(TTheArgs_...) cv_qual \
+    > struct sfinae {}; \
+    template <typename UTheClass_> \
+    constexpr static bool test(sfinae<UTheClass_, &UTheClass_::func_name>*) \
+    { return true; } \
+    template <typename> \
+    constexpr static bool test(...) { return false; } \
+  public: \
+    constexpr static bool value = test<TTheClass_>(nullptr); \
+  }
+
+/*
+ * The FOLLY_CREATE_HAS_MEMBER_FN_TRAITS is used to create traits
+ * classes that check for the existence of a member function with
+ * a given name and signature. It currently does not support
+ * checking for inherited members.
+ *
+ * Such classes receive two template parameters: the class to be checked
+ * and the signature of the member function. A static boolean field
+ * named `value` (which is also constexpr) tells whether such member
+ * function exists.
+ *
+ * Each traits class created is bound only to the member name, not to
+ * its signature nor to the type of the class containing it.
+ *
+ * Say you need to know if a given class has a member function named
+ * `test` with the following signature:
+ *
+ *    int test() const;
+ *
+ * You'd need this macro to create a traits class to check for a member
+ * named `test`, and then use this traits class to check for the signature:
+ *
+ * namespace {
+ *
+ * FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
+ *
+ * } // unnamed-namespace
+ *
+ * void some_func() {
+ *   cout << "Does class Foo have a member int test() const? "
+ *     << boolalpha << has_test_traits<Foo, int() const>::value;
+ * }
+ *
+ * You can use the same traits class to test for a completely different
+ * signature, on a completely different class, as long as the member name
+ * is the same:
+ *
+ * void some_func() {
+ *   cout << "Does class Foo have a member int test()? "
+ *     << boolalpha << has_test_traits<Foo, int()>::value;
+ *   cout << "Does class Foo have a member int test() const? "
+ *     << boolalpha << has_test_traits<Foo, int() const>::value;
+ *   cout << "Does class Bar have a member double test(const string&, long)? "
+ *     << boolalpha << has_test_traits<Bar, double(const string&, long)>::value;
+ * }
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(classname, func_name) \
+  template <typename, typename> class classname; \
+  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, ); \
+  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, const); \
+  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL( \
+      classname, func_name, /* nolint */ volatile); \
+  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL( \
+      classname, func_name, /* nolint */ volatile const)
+
+#endif //FOLLY_BASE_TRAITS_H_
diff --git a/faux-folly/folly/VERSION b/faux-folly/folly/VERSION
new file mode 100644
index 0000000..c68f108
--- /dev/null
+++ b/faux-folly/folly/VERSION
@@ -0,0 +1 @@
+57:0
\ No newline at end of file
diff --git a/faux-folly/folly/Varint.h b/faux-folly/folly/Varint.h
new file mode 100644
index 0000000..e162671
--- /dev/null
+++ b/faux-folly/folly/Varint.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_VARINT_H_
+#define FOLLY_VARINT_H_
+
+#include <type_traits>
+#include <folly/Conv.h>
+#include <folly/Range.h>
+
+namespace folly {
+
+/**
+ * Variable-length integer encoding, using a little-endian, base-128
+ * representation.
+ *
+ * The MSb is set on all bytes except the last.
+ *
+ * Details:
+ * https://developers.google.com/protocol-buffers/docs/encoding#varints
+ *
+ * If you want to encode multiple values, GroupVarint (in GroupVarint.h)
+ * is faster and likely smaller.
+ */
+
+/**
+ * Maximum length (in bytes) of the varint encoding of a 32-bit value.
+ */
+constexpr size_t kMaxVarintLength32 = 5;
+
+/**
+ * Maximum length (in bytes) of the varint encoding of a 64-bit value.
+ */
+constexpr size_t kMaxVarintLength64 = 10;
+
+/**
+ * Encode a value in the given buffer, returning the number of bytes used
+ * for encoding.
+ * buf must have enough space to represent the value (at least
+ * kMaxVarintLength64 bytes to encode arbitrary 64-bit values)
+ */
+size_t encodeVarint(uint64_t val, uint8_t* buf);
+
+/**
+ * Decode a value from a given buffer, advances data past the returned value.
+ */
+template <class T>
+uint64_t decodeVarint(Range<T*>& data);
+
+/**
+ * ZigZag encoding that maps signed integers with a small absolute value
+ * to unsigned integers with a small (positive) values. Without this,
+ * encoding negative values using Varint would use up 9 or 10 bytes.
+ *
+ * if x >= 0, encodeZigZag(x) == 2*x
+ * if x <  0, encodeZigZag(x) == -2*x + 1
+ */
+
+inline uint64_t encodeZigZag(int64_t val) {
+  // Bit-twiddling magic stolen from the Google protocol buffer document;
+  // val >> 63 is an arithmetic shift because val is signed
+  return static_cast<uint64_t>((val << 1) ^ (val >> 63));
+}
+
+inline int64_t decodeZigZag(uint64_t val) {
+  return static_cast<int64_t>((val >> 1) ^ -(val & 1));
+}
+
+// Implementation below
+
+inline size_t encodeVarint(uint64_t val, uint8_t* buf) {
+  uint8_t* p = buf;
+  while (val >= 128) {
+    *p++ = 0x80 | (val & 0x7f);
+    val >>= 7;
+  }
+  *p++ = val;
+  return p - buf;
+}
+
+template <class T>
+inline uint64_t decodeVarint(Range<T*>& data) {
+  static_assert(
+      std::is_same<typename std::remove_cv<T>::type, char>::value ||
+          std::is_same<typename std::remove_cv<T>::type, unsigned char>::value,
+      "Only character ranges are supported");
+
+  const int8_t* begin = reinterpret_cast<const int8_t*>(data.begin());
+  const int8_t* end = reinterpret_cast<const int8_t*>(data.end());
+  const int8_t* p = begin;
+  uint64_t val = 0;
+
+  // end is always greater than or equal to begin, so this subtraction is safe
+  if (LIKELY(size_t(end - begin) >= kMaxVarintLength64)) {  // fast path
+    int64_t b;
+    do {
+      b = *p++; val  = (b & 0x7f)      ; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) <<  7; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 14; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 21; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 28; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 35; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 42; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 49; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 56; if (b >= 0) break;
+      b = *p++; val |= (b & 0x7f) << 63; if (b >= 0) break;
+      throw std::invalid_argument("Invalid varint value. Too big.");
+    } while (false);
+  } else {
+    int shift = 0;
+    while (p != end && *p < 0) {
+      val |= static_cast<uint64_t>(*p++ & 0x7f) << shift;
+      shift += 7;
+    }
+    if (p == end) {
+      throw std::invalid_argument("Invalid varint value. Too small: " +
+                                  folly::to<std::string>(end - begin) +
+                                  " bytes");
+    }
+    val |= static_cast<uint64_t>(*p++) << shift;
+  }
+
+  data.advance(p - begin);
+  return val;
+}
+
+}  // namespaces
+
+#endif /* FOLLY_VARINT_H_ */
diff --git a/faux-folly/folly/Version.cpp b/faux-folly/folly/Version.cpp
new file mode 100644
index 0000000..06d133b
--- /dev/null
+++ b/faux-folly/folly/Version.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 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/VersionCheck.h>
+
+namespace folly { namespace detail {
+
+FOLLY_VERSION_CHECK(folly, FOLLY_VERSION)
+
+}}  // namespaces
diff --git a/faux-folly/folly/VersionCheck.h b/faux-folly/folly/VersionCheck.h
new file mode 100644
index 0000000..988ea2c
--- /dev/null
+++ b/faux-folly/folly/VersionCheck.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_VERSIONCHECK_H_
+#define FOLLY_VERSIONCHECK_H_
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <folly/Portability.h>
+#include <folly/Preprocessor.h>
+
+/**
+ * Check if the currently loaded version of a library is what you expect.
+ *
+ * It is possible for multiple versions of the same shared library to end up
+ * being loaded simultaneously in the same address space, usually with
+ * disastrous results.
+ *
+ * For example, let's say you have a shared library (foo) that doesn't keep
+ * binary compatbility between releases, and so each version is distributed as
+ * a SO with different SONAME. Let's say you build another shared library, bar
+ * that depends on version 1 of foo: libbar.so depends on libfoo1.so.
+ * Your main executable now (baz) depends on version 2 of foo, and also
+ * depends on bar: baz depends on libfoo2.so and libbar.so.
+ *
+ * At load time, baz loads libfoo2.so first, then libbar.so; libbar.so will
+ * load libfoo1.so, but, as this is normal dynamic loading (and not explicit
+ * dlopen calls with RTLD_DEEPBIND), any symbols from libfoo1.so that are
+ * also present in libfoo2.so will be satisfied from the (already loaded)
+ * libfoo2.so.
+ *
+ * But foo does not preserve binary compatibility between versions, so all
+ * hell breaks loose (the symbols from libfoo2.so are not necessarily direct
+ * replacements of the identically-named symbols in libfoo1.so).
+ *
+ * It is better to crash with a helpful error message instead, which is what
+ * this macro provides. FOLLY_VERSION_CHECK verifies at load time that
+ * the compiled-in version is the same as the currently loaded version.
+ *
+ * Usage: use this macro at namespace scope in a .cpp file (IMPORTANT: NOT
+ * in the unnamed namespace):
+ *
+ * FOLLY_VERSION_CHECK(mylib, "1")
+ *
+ * The first argument identifies your library; the second argument is a
+ * string literal containing the desired version string.
+ *
+ * In order to avoid changing the file for each version, the version string
+ * could be provided on the compiler command line with -D:
+ *
+ * FOLLY_VERSION_CHECK(mylib, MYLIB_VERSION)
+ *
+ * ... and then commpile your file with -DMYLIB_VERSION=\"1\"
+ */
+
+#if defined(_MSC_VER)
+// MSVC doesn't support constructor priorities. Just pray it works, I guess.
+// We could implement a link-time mechanism for MSVC,
+// via #pragma detect_mismatch but that would only handle
+// static library linking.
+# define FOLLY_VERSION_CHECK_PRIORITY(Ret, name) \
+    __pragma(section(".CRT$XCU",read)) \
+    static Ret __cdecl name(void); \
+    __declspec(allocate(".CRT$XCU")) \
+    Ret (__cdecl*name##_)(void) = name; \
+    Ret __cdecl name()
+
+#elif defined(__APPLE__)
+// OS X doesn't support constructor priorities. Just pray it works, I guess.
+# define FOLLY_VERSION_CHECK_PRIORITY(Ret, name) \
+  __attribute__((__constructor__)) Ret name()
+
+#else
+# define FOLLY_VERSION_CHECK_PRIORITY(Ret, name) \
+  __attribute__((__constructor__(101))) Ret name()
+#endif
+
+// Note that this is carefully crafted: PRODUCT##Version must have external
+// linkage (so it collides among versions), versionCheck must have internal
+// linkage (so it does NOT collide between versions); if we're trying to have
+// multiple versions loaded at the same time, they must each run their copy
+// of versionCheck, but share the PRODUCT##Version variable.
+#define FOLLY_VERSION_CHECK(PRODUCT, VERSION) \
+  const char* PRODUCT##Version = VERSION; \
+  namespace { \
+  FOLLY_VERSION_CHECK_PRIORITY(void, versionCheck) { \
+    if (strcmp(PRODUCT##Version, VERSION)) { \
+      fprintf(stderr, \
+              "Invalid %s version: desired [%s], currently loaded [%s]\n", \
+              FB_STRINGIZE(PRODUCT), PRODUCT##Version, VERSION); \
+      abort(); \
+    } \
+  } \
+  }
+
+#endif /* FOLLY_VERSIONCHECK_H_ */
diff --git a/faux-folly/folly/android/build/faux-folly-common.mk b/faux-folly/folly/android/build/faux-folly-common.mk
new file mode 100644
index 0000000..78af156
--- /dev/null
+++ b/faux-folly/folly/android/build/faux-folly-common.mk
@@ -0,0 +1,17 @@
+LOCAL_CLANG := false
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CPPFLAGS += -fexceptions
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/android/include-private \
+	$(LOCAL_PATH)/.. \
+	external/boost \
+	external/google-gflags/android \
+	external/google-gflags/src \
+	external/google-glog/android/include \
+	external/google-glog/src \
+	external/google-double-conversion/android/include
+LOCAL_SHARED_LIBRARIES += libc++ \
+	libdouble-conversion \
+	libgflags \
+	libglog
diff --git a/faux-folly/folly/android/build/faux-folly-test-case.mk b/faux-folly/folly/android/build/faux-folly-test-case.mk
new file mode 100644
index 0000000..badd37a
--- /dev/null
+++ b/faux-folly/folly/android/build/faux-folly-test-case.mk
@@ -0,0 +1,14 @@
+include $(LOCAL_PATH)/android/build/faux-folly-common.mk
+LOCAL_C_INCLUDES += \
+	external/gtest/include \
+	$(LOCAL_PATH)/test
+LOCAL_STATIC_LIBRARIES += \
+	libgtest_main \
+	libgtest \
+	libfollybenchmark
+LOCAL_SHARED_LIBRARIES += \
+	libfolly \
+	libboost_regex
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/faux-folly/bin
+FAUX_FOLLY_UNIT_TESTS += $(LOCAL_MODULE)
+include $(BUILD_EXECUTABLE)
diff --git a/faux-folly/folly/android/include-private/folly/folly-config.h b/faux-folly/folly/android/include-private/folly/folly-config.h
new file mode 100644
index 0000000..d96158c
--- /dev/null
+++ b/faux-folly/folly/android/include-private/folly/folly-config.h
@@ -0,0 +1,279 @@
+#ifndef _FOLLY_CONFIG_H
+#define _FOLLY_CONFIG_H 1
+
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to "final" if the compiler supports C++11 "final" */
+#define FOLLY_FINAL final
+
+/* Define to gflags namespace (usually "google" or "gflags") */
+#define FOLLY_GFLAGS_NAMESPACE google
+
+/* Define to 1 if you have the <bits/c++config.h> header file. */
+/* #undef FOLLY_HAVE_BITS_C__CONFIG_H */
+
+/* Define to 1 if you have the <bits/functexcept.h> header file. */
+/* #undef FOLLY_HAVE_BITS_FUNCTEXCEPT_H */
+
+/* define if the Boost library is available */
+#define FOLLY_HAVE_BOOST 1
+
+/* define if the Boost::Context library is available */
+/* #undef FOLLY_HAVE_BOOST_CONTEXT */
+
+/* define if the Boost::Filesystem library is available */
+/* #undef FOLLY_HAVE_BOOST_FILESYSTEM */
+
+/* define if the Boost::PROGRAM_OPTIONS library is available */
+/* #undef FOLLY_HAVE_BOOST_PROGRAM_OPTIONS */
+
+/* define if the Boost::Regex library is available */
+#define FOLLY_HAVE_BOOST_REGEX 1
+
+/* define if the Boost::System library is available */
+#define FOLLY_HAVE_BOOST_SYSTEM 1
+
+/* define if the Boost::Thread library is available */
+#define FOLLY_HAVE_BOOST_THREAD 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#define FOLLY_HAVE_BYTESWAP_H 1
+
+/* Define to 1 if we support clock_gettime(2). */
+#define FOLLY_HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if strlen(3) is constexpr. */
+/* #undef FOLLY_HAVE_CONSTEXPR_STRLEN */
+
+/* Define to 1 if we have cplus_demangle_v3_callback. */
+/* #undef FOLLY_HAVE_CPLUS_DEMANGLE_V3_CALLBACK */
+
+/* Define if you want to support deprecated associative containers */
+/* #undef FOLLY_HAVE_DEPRECATED_ASSOC */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define FOLLY_HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <emmintrin.h> header file. */
+#if defined(__i386__) || defined(__x86_64__)
+#define FOLLY_HAVE_EMMINTRIN_H 1
+#else
+/* #undef FOLLY_HAVE_EMMINTRIN_H */
+#endif
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define FOLLY_HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <features.h> header file. */
+#define FOLLY_HAVE_FEATURES_H 1
+
+/* Define to 1 if you have the `getdelim' function. */
+#define FOLLY_HAVE_GETDELIM 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define FOLLY_HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if the compiler supports ifunc */
+#define FOLLY_HAVE_IFUNC 1
+
+/* Define if we have __int128 */
+/* #undef FOLLY_HAVE_INT128_T */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define FOLLY_HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `double-conversion' library
+   (-ldouble-conversion). */
+#define FOLLY_HAVE_LIBDOUBLE_CONVERSION 1
+
+/* Define to 1 if you have the `event' library (-levent). */
+#define FOLLY_HAVE_LIBEVENT 1
+
+/* Define to 1 if you have the `gflags' library (-lgflags). */
+#define FOLLY_HAVE_LIBGFLAGS 1
+
+/* Define to 1 if you have the `glog' library (-lglog). */
+#define FOLLY_HAVE_LIBGLOG 1
+
+/* Define to 1 if you have the `jemalloc' library (-ljemalloc). */
+#define FOLLY_HAVE_LIBJEMALLOC 1
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+#define FOLLY_HAVE_LIBLZ4 1
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+#define FOLLY_HAVE_LIBLZMA 1
+
+/* Define to 1 if you have the `snappy' library (-lsnappy). */
+#define FOLLY_HAVE_LIBSNAPPY 1
+
+/* Define to 1 if you have the `ssl' library (-lssl). */
+#define FOLLY_HAVE_LIBSSL 1
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define FOLLY_HAVE_LIBZ 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define FOLLY_HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define FOLLY_HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the `malloc_size' function. */
+/* #undef FOLLY_HAVE_MALLOC_SIZE */
+
+/* Define to 1 if you have the `malloc_usable_size' function. */
+#define FOLLY_HAVE_MALLOC_USABLE_SIZE 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define FOLLY_HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define FOLLY_HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memrchr' function. */
+#define FOLLY_HAVE_MEMRCHR 1
+
+/* Define to 1 if you have the `memset' function. */
+#define FOLLY_HAVE_MEMSET 1
+
+/* Define to 1 if you have the <mutex.h> header file. */
+/* #undef FOLLY_HAVE_MUTEX_H */
+
+/* Define to 1 if you have the `pipe2' function. */
+#define FOLLY_HAVE_PIPE2 1
+
+/* Define to 1 if you have the `pow' function. */
+#define FOLLY_HAVE_POW 1
+
+/* Define to 1 if the compiler supports pthread_atfork */
+#define FOLLY_HAVE_PTHREAD_ATFORK 1
+
+/* Define to 1 if the system has the type `pthread_spinlock_t'. */
+/* #undef FOLLY_HAVE_PTHREAD_SPINLOCK_T */
+
+/* Define to 1 if you have the `pthread_yield' function. */
+/* #undef FOLLY_HAVE_PTHREAD_YIELD */
+
+/* Define to 1 if the system has the type `ptrdiff_t'. */
+/* #undef FOLLY_HAVE_PTRDIFF_T */
+
+/* Define to 1 if you have the <sched.h> header file. */
+#define FOLLY_HAVE_SCHED_H 1
+
+/* Define to 1 if you have the `sched_yield' function. */
+#define FOLLY_HAVE_SCHED_YIELD 1
+
+/* Define to 1 if stdbool.h conforms to C99. */
+/* #undef FOLLY_HAVE_STDBOOL_H */
+
+/* Define if g++ supports C++0x features. */
+#define FOLLY_HAVE_STDCXX_0X 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define FOLLY_HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define FOLLY_HAVE_STDLIB_H 1
+
+/* Define to 1 if we have a usable std::is_trivially_copyable<T>
+   implementation. */
+#define FOLLY_HAVE_STD__IS_TRIVIALLY_COPYABLE 1
+
+/* Define to 1 if std::this_thread::sleep_for() is defined. */
+#define FOLLY_HAVE_STD__THIS_THREAD__SLEEP_FOR 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define FOLLY_HAVE_STRERROR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define FOLLY_HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define FOLLY_HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define FOLLY_HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define FOLLY_HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define FOLLY_HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define FOLLY_HAVE_UNISTD_H 1
+
+/* Define to 1 if the vsnprintf supports returning errors on bad format
+   strings. */
+#define FOLLY_HAVE_VSNPRINTF_ERRORS 1
+
+/* Define to 1 if the linker supports weak symbols. */
+#define FOLLY_HAVE_WEAK_SYMBOLS 1
+
+/* Define to 1 if the runtime supports XSI-style strerror_r */
+/* #undef FOLLY_HAVE_XSI_STRERROR_R */
+
+/* Define to 1 if the system has the type `_Bool'. */
+#define FOLLY_HAVE__BOOL 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define FOLLY_LT_OBJDIR ".lib/"
+
+/* Define to "override" if the compiler supports C++11 "override" */
+#define FOLLY_OVERRIDE override
+
+/* Name of package */
+#define FOLLY_PACKAGE "folly"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define FOLLY_PACKAGE_BUGREPORT "folly@fb.com"
+
+/* Define to the full name of this package. */
+#define FOLLY_PACKAGE_NAME "folly"
+
+/* Define to the full name and version of this package. */
+#define FOLLY_PACKAGE_STRING "folly 52.0"
+
+/* Define to the one symbol short name of this package. */
+#define FOLLY_PACKAGE_TARNAME "folly"
+
+/* Define to the home page for this package. */
+#define FOLLY_PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define FOLLY_PACKAGE_VERSION "52.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define FOLLY_STDC_HEADERS 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define FOLLY_TIME_WITH_SYS_TIME 1
+
+/* Define to 1 if the gflags namespace is not "gflags" */
+#define FOLLY_UNUSUAL_GFLAGS_NAMESPACE 1
+
+/* Define to 1 if we're using libc++. */
+#define FOLLY_USE_LIBCPP 1
+
+/* Version number of package */
+#define FOLLY_VERSION "52.0"
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+   code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
+
+#endif
diff --git a/faux-folly/folly/android/src/EscapeTables.cpp b/faux-folly/folly/android/src/EscapeTables.cpp
new file mode 100644
index 0000000..5120e92
--- /dev/null
+++ b/faux-folly/folly/android/src/EscapeTables.cpp
@@ -0,0 +1,55 @@
+namespace folly {
+namespace detail {
+
+extern const char cEscapeTable[] =
+  "OOOOOOOOOtnOOrOOOOOOOOOOOOOOOOOOPP\"PPPPPPPPPPPPPPPPPPPPPPPPPPPP?"
+  "PPPPPPPPPPPPPPPPPPPPPPPPPPPP\\PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO"
+  "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
+  "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO";
+
+extern const char cUnescapeTable[] =
+  "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\"IIII'IIIIIIIIOOOOOOOOIIIIIII?"
+  "IIIIIIIIIIIIIIIIIIIIIIIIIIII\\IIII\a\bIII\fIIIIIII\nIII\rI\tI\vIXIIIIIII"
+  "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII"
+  "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII";
+
+extern const unsigned char hexTable[] = {
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 16, 16, 16, 16, 16, 16, 
+  16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
+};
+
+extern const unsigned char uriEscapeTable[] = {
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 2, 
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 
+  4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 
+  4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+};
+
+}  // namespace detail
+}  // namespace folly
diff --git a/faux-folly/folly/android/src/FormatTables.cpp b/faux-folly/folly/android/src/FormatTables.cpp
new file mode 100644
index 0000000..fc02bce
--- /dev/null
+++ b/faux-folly/folly/android/src/FormatTables.cpp
@@ -0,0 +1,751 @@
+#include <folly/FormatArg.h>
+
+namespace folly {
+namespace detail {
+
+extern const FormatArg::Align formatAlignTable[] = {
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::LEFT, FormatArg::Align::PAD_AFTER_SIGN, 
+  FormatArg::Align::RIGHT, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::CENTER, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+  FormatArg::Align::INVALID, FormatArg::Align::INVALID, 
+};
+
+extern const FormatArg::Sign formatSignTable[] = {
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::SPACE_OR_MINUS, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::PLUS_OR_MINUS, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::MINUS, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+  FormatArg::Sign::INVALID, FormatArg::Sign::INVALID, 
+};
+
+extern const char formatOctal[512][3] = {
+{'0', '0', '0'}, {'0', '0', '1'}, {'0', '0', '2'}, {'0', '0', '3'}, 
+{'0', '0', '4'}, {'0', '0', '5'}, {'0', '0', '6'}, {'0', '0', '7'}, 
+{'0', '1', '0'}, {'0', '1', '1'}, {'0', '1', '2'}, {'0', '1', '3'}, 
+{'0', '1', '4'}, {'0', '1', '5'}, {'0', '1', '6'}, {'0', '1', '7'}, 
+{'0', '2', '0'}, {'0', '2', '1'}, {'0', '2', '2'}, {'0', '2', '3'}, 
+{'0', '2', '4'}, {'0', '2', '5'}, {'0', '2', '6'}, {'0', '2', '7'}, 
+{'0', '3', '0'}, {'0', '3', '1'}, {'0', '3', '2'}, {'0', '3', '3'}, 
+{'0', '3', '4'}, {'0', '3', '5'}, {'0', '3', '6'}, {'0', '3', '7'}, 
+{'0', '4', '0'}, {'0', '4', '1'}, {'0', '4', '2'}, {'0', '4', '3'}, 
+{'0', '4', '4'}, {'0', '4', '5'}, {'0', '4', '6'}, {'0', '4', '7'}, 
+{'0', '5', '0'}, {'0', '5', '1'}, {'0', '5', '2'}, {'0', '5', '3'}, 
+{'0', '5', '4'}, {'0', '5', '5'}, {'0', '5', '6'}, {'0', '5', '7'}, 
+{'0', '6', '0'}, {'0', '6', '1'}, {'0', '6', '2'}, {'0', '6', '3'}, 
+{'0', '6', '4'}, {'0', '6', '5'}, {'0', '6', '6'}, {'0', '6', '7'}, 
+{'0', '7', '0'}, {'0', '7', '1'}, {'0', '7', '2'}, {'0', '7', '3'}, 
+{'0', '7', '4'}, {'0', '7', '5'}, {'0', '7', '6'}, {'0', '7', '7'}, 
+{'1', '0', '0'}, {'1', '0', '1'}, {'1', '0', '2'}, {'1', '0', '3'}, 
+{'1', '0', '4'}, {'1', '0', '5'}, {'1', '0', '6'}, {'1', '0', '7'}, 
+{'1', '1', '0'}, {'1', '1', '1'}, {'1', '1', '2'}, {'1', '1', '3'}, 
+{'1', '1', '4'}, {'1', '1', '5'}, {'1', '1', '6'}, {'1', '1', '7'}, 
+{'1', '2', '0'}, {'1', '2', '1'}, {'1', '2', '2'}, {'1', '2', '3'}, 
+{'1', '2', '4'}, {'1', '2', '5'}, {'1', '2', '6'}, {'1', '2', '7'}, 
+{'1', '3', '0'}, {'1', '3', '1'}, {'1', '3', '2'}, {'1', '3', '3'}, 
+{'1', '3', '4'}, {'1', '3', '5'}, {'1', '3', '6'}, {'1', '3', '7'}, 
+{'1', '4', '0'}, {'1', '4', '1'}, {'1', '4', '2'}, {'1', '4', '3'}, 
+{'1', '4', '4'}, {'1', '4', '5'}, {'1', '4', '6'}, {'1', '4', '7'}, 
+{'1', '5', '0'}, {'1', '5', '1'}, {'1', '5', '2'}, {'1', '5', '3'}, 
+{'1', '5', '4'}, {'1', '5', '5'}, {'1', '5', '6'}, {'1', '5', '7'}, 
+{'1', '6', '0'}, {'1', '6', '1'}, {'1', '6', '2'}, {'1', '6', '3'}, 
+{'1', '6', '4'}, {'1', '6', '5'}, {'1', '6', '6'}, {'1', '6', '7'}, 
+{'1', '7', '0'}, {'1', '7', '1'}, {'1', '7', '2'}, {'1', '7', '3'}, 
+{'1', '7', '4'}, {'1', '7', '5'}, {'1', '7', '6'}, {'1', '7', '7'}, 
+{'2', '0', '0'}, {'2', '0', '1'}, {'2', '0', '2'}, {'2', '0', '3'}, 
+{'2', '0', '4'}, {'2', '0', '5'}, {'2', '0', '6'}, {'2', '0', '7'}, 
+{'2', '1', '0'}, {'2', '1', '1'}, {'2', '1', '2'}, {'2', '1', '3'}, 
+{'2', '1', '4'}, {'2', '1', '5'}, {'2', '1', '6'}, {'2', '1', '7'}, 
+{'2', '2', '0'}, {'2', '2', '1'}, {'2', '2', '2'}, {'2', '2', '3'}, 
+{'2', '2', '4'}, {'2', '2', '5'}, {'2', '2', '6'}, {'2', '2', '7'}, 
+{'2', '3', '0'}, {'2', '3', '1'}, {'2', '3', '2'}, {'2', '3', '3'}, 
+{'2', '3', '4'}, {'2', '3', '5'}, {'2', '3', '6'}, {'2', '3', '7'}, 
+{'2', '4', '0'}, {'2', '4', '1'}, {'2', '4', '2'}, {'2', '4', '3'}, 
+{'2', '4', '4'}, {'2', '4', '5'}, {'2', '4', '6'}, {'2', '4', '7'}, 
+{'2', '5', '0'}, {'2', '5', '1'}, {'2', '5', '2'}, {'2', '5', '3'}, 
+{'2', '5', '4'}, {'2', '5', '5'}, {'2', '5', '6'}, {'2', '5', '7'}, 
+{'2', '6', '0'}, {'2', '6', '1'}, {'2', '6', '2'}, {'2', '6', '3'}, 
+{'2', '6', '4'}, {'2', '6', '5'}, {'2', '6', '6'}, {'2', '6', '7'}, 
+{'2', '7', '0'}, {'2', '7', '1'}, {'2', '7', '2'}, {'2', '7', '3'}, 
+{'2', '7', '4'}, {'2', '7', '5'}, {'2', '7', '6'}, {'2', '7', '7'}, 
+{'3', '0', '0'}, {'3', '0', '1'}, {'3', '0', '2'}, {'3', '0', '3'}, 
+{'3', '0', '4'}, {'3', '0', '5'}, {'3', '0', '6'}, {'3', '0', '7'}, 
+{'3', '1', '0'}, {'3', '1', '1'}, {'3', '1', '2'}, {'3', '1', '3'}, 
+{'3', '1', '4'}, {'3', '1', '5'}, {'3', '1', '6'}, {'3', '1', '7'}, 
+{'3', '2', '0'}, {'3', '2', '1'}, {'3', '2', '2'}, {'3', '2', '3'}, 
+{'3', '2', '4'}, {'3', '2', '5'}, {'3', '2', '6'}, {'3', '2', '7'}, 
+{'3', '3', '0'}, {'3', '3', '1'}, {'3', '3', '2'}, {'3', '3', '3'}, 
+{'3', '3', '4'}, {'3', '3', '5'}, {'3', '3', '6'}, {'3', '3', '7'}, 
+{'3', '4', '0'}, {'3', '4', '1'}, {'3', '4', '2'}, {'3', '4', '3'}, 
+{'3', '4', '4'}, {'3', '4', '5'}, {'3', '4', '6'}, {'3', '4', '7'}, 
+{'3', '5', '0'}, {'3', '5', '1'}, {'3', '5', '2'}, {'3', '5', '3'}, 
+{'3', '5', '4'}, {'3', '5', '5'}, {'3', '5', '6'}, {'3', '5', '7'}, 
+{'3', '6', '0'}, {'3', '6', '1'}, {'3', '6', '2'}, {'3', '6', '3'}, 
+{'3', '6', '4'}, {'3', '6', '5'}, {'3', '6', '6'}, {'3', '6', '7'}, 
+{'3', '7', '0'}, {'3', '7', '1'}, {'3', '7', '2'}, {'3', '7', '3'}, 
+{'3', '7', '4'}, {'3', '7', '5'}, {'3', '7', '6'}, {'3', '7', '7'}, 
+{'4', '0', '0'}, {'4', '0', '1'}, {'4', '0', '2'}, {'4', '0', '3'}, 
+{'4', '0', '4'}, {'4', '0', '5'}, {'4', '0', '6'}, {'4', '0', '7'}, 
+{'4', '1', '0'}, {'4', '1', '1'}, {'4', '1', '2'}, {'4', '1', '3'}, 
+{'4', '1', '4'}, {'4', '1', '5'}, {'4', '1', '6'}, {'4', '1', '7'}, 
+{'4', '2', '0'}, {'4', '2', '1'}, {'4', '2', '2'}, {'4', '2', '3'}, 
+{'4', '2', '4'}, {'4', '2', '5'}, {'4', '2', '6'}, {'4', '2', '7'}, 
+{'4', '3', '0'}, {'4', '3', '1'}, {'4', '3', '2'}, {'4', '3', '3'}, 
+{'4', '3', '4'}, {'4', '3', '5'}, {'4', '3', '6'}, {'4', '3', '7'}, 
+{'4', '4', '0'}, {'4', '4', '1'}, {'4', '4', '2'}, {'4', '4', '3'}, 
+{'4', '4', '4'}, {'4', '4', '5'}, {'4', '4', '6'}, {'4', '4', '7'}, 
+{'4', '5', '0'}, {'4', '5', '1'}, {'4', '5', '2'}, {'4', '5', '3'}, 
+{'4', '5', '4'}, {'4', '5', '5'}, {'4', '5', '6'}, {'4', '5', '7'}, 
+{'4', '6', '0'}, {'4', '6', '1'}, {'4', '6', '2'}, {'4', '6', '3'}, 
+{'4', '6', '4'}, {'4', '6', '5'}, {'4', '6', '6'}, {'4', '6', '7'}, 
+{'4', '7', '0'}, {'4', '7', '1'}, {'4', '7', '2'}, {'4', '7', '3'}, 
+{'4', '7', '4'}, {'4', '7', '5'}, {'4', '7', '6'}, {'4', '7', '7'}, 
+{'5', '0', '0'}, {'5', '0', '1'}, {'5', '0', '2'}, {'5', '0', '3'}, 
+{'5', '0', '4'}, {'5', '0', '5'}, {'5', '0', '6'}, {'5', '0', '7'}, 
+{'5', '1', '0'}, {'5', '1', '1'}, {'5', '1', '2'}, {'5', '1', '3'}, 
+{'5', '1', '4'}, {'5', '1', '5'}, {'5', '1', '6'}, {'5', '1', '7'}, 
+{'5', '2', '0'}, {'5', '2', '1'}, {'5', '2', '2'}, {'5', '2', '3'}, 
+{'5', '2', '4'}, {'5', '2', '5'}, {'5', '2', '6'}, {'5', '2', '7'}, 
+{'5', '3', '0'}, {'5', '3', '1'}, {'5', '3', '2'}, {'5', '3', '3'}, 
+{'5', '3', '4'}, {'5', '3', '5'}, {'5', '3', '6'}, {'5', '3', '7'}, 
+{'5', '4', '0'}, {'5', '4', '1'}, {'5', '4', '2'}, {'5', '4', '3'}, 
+{'5', '4', '4'}, {'5', '4', '5'}, {'5', '4', '6'}, {'5', '4', '7'}, 
+{'5', '5', '0'}, {'5', '5', '1'}, {'5', '5', '2'}, {'5', '5', '3'}, 
+{'5', '5', '4'}, {'5', '5', '5'}, {'5', '5', '6'}, {'5', '5', '7'}, 
+{'5', '6', '0'}, {'5', '6', '1'}, {'5', '6', '2'}, {'5', '6', '3'}, 
+{'5', '6', '4'}, {'5', '6', '5'}, {'5', '6', '6'}, {'5', '6', '7'}, 
+{'5', '7', '0'}, {'5', '7', '1'}, {'5', '7', '2'}, {'5', '7', '3'}, 
+{'5', '7', '4'}, {'5', '7', '5'}, {'5', '7', '6'}, {'5', '7', '7'}, 
+{'6', '0', '0'}, {'6', '0', '1'}, {'6', '0', '2'}, {'6', '0', '3'}, 
+{'6', '0', '4'}, {'6', '0', '5'}, {'6', '0', '6'}, {'6', '0', '7'}, 
+{'6', '1', '0'}, {'6', '1', '1'}, {'6', '1', '2'}, {'6', '1', '3'}, 
+{'6', '1', '4'}, {'6', '1', '5'}, {'6', '1', '6'}, {'6', '1', '7'}, 
+{'6', '2', '0'}, {'6', '2', '1'}, {'6', '2', '2'}, {'6', '2', '3'}, 
+{'6', '2', '4'}, {'6', '2', '5'}, {'6', '2', '6'}, {'6', '2', '7'}, 
+{'6', '3', '0'}, {'6', '3', '1'}, {'6', '3', '2'}, {'6', '3', '3'}, 
+{'6', '3', '4'}, {'6', '3', '5'}, {'6', '3', '6'}, {'6', '3', '7'}, 
+{'6', '4', '0'}, {'6', '4', '1'}, {'6', '4', '2'}, {'6', '4', '3'}, 
+{'6', '4', '4'}, {'6', '4', '5'}, {'6', '4', '6'}, {'6', '4', '7'}, 
+{'6', '5', '0'}, {'6', '5', '1'}, {'6', '5', '2'}, {'6', '5', '3'}, 
+{'6', '5', '4'}, {'6', '5', '5'}, {'6', '5', '6'}, {'6', '5', '7'}, 
+{'6', '6', '0'}, {'6', '6', '1'}, {'6', '6', '2'}, {'6', '6', '3'}, 
+{'6', '6', '4'}, {'6', '6', '5'}, {'6', '6', '6'}, {'6', '6', '7'}, 
+{'6', '7', '0'}, {'6', '7', '1'}, {'6', '7', '2'}, {'6', '7', '3'}, 
+{'6', '7', '4'}, {'6', '7', '5'}, {'6', '7', '6'}, {'6', '7', '7'}, 
+{'7', '0', '0'}, {'7', '0', '1'}, {'7', '0', '2'}, {'7', '0', '3'}, 
+{'7', '0', '4'}, {'7', '0', '5'}, {'7', '0', '6'}, {'7', '0', '7'}, 
+{'7', '1', '0'}, {'7', '1', '1'}, {'7', '1', '2'}, {'7', '1', '3'}, 
+{'7', '1', '4'}, {'7', '1', '5'}, {'7', '1', '6'}, {'7', '1', '7'}, 
+{'7', '2', '0'}, {'7', '2', '1'}, {'7', '2', '2'}, {'7', '2', '3'}, 
+{'7', '2', '4'}, {'7', '2', '5'}, {'7', '2', '6'}, {'7', '2', '7'}, 
+{'7', '3', '0'}, {'7', '3', '1'}, {'7', '3', '2'}, {'7', '3', '3'}, 
+{'7', '3', '4'}, {'7', '3', '5'}, {'7', '3', '6'}, {'7', '3', '7'}, 
+{'7', '4', '0'}, {'7', '4', '1'}, {'7', '4', '2'}, {'7', '4', '3'}, 
+{'7', '4', '4'}, {'7', '4', '5'}, {'7', '4', '6'}, {'7', '4', '7'}, 
+{'7', '5', '0'}, {'7', '5', '1'}, {'7', '5', '2'}, {'7', '5', '3'}, 
+{'7', '5', '4'}, {'7', '5', '5'}, {'7', '5', '6'}, {'7', '5', '7'}, 
+{'7', '6', '0'}, {'7', '6', '1'}, {'7', '6', '2'}, {'7', '6', '3'}, 
+{'7', '6', '4'}, {'7', '6', '5'}, {'7', '6', '6'}, {'7', '6', '7'}, 
+{'7', '7', '0'}, {'7', '7', '1'}, {'7', '7', '2'}, {'7', '7', '3'}, 
+{'7', '7', '4'}, {'7', '7', '5'}, {'7', '7', '6'}, {'7', '7', '7'}, 
+};
+
+extern const char formatHexLower[256][2] = {
+{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, 
+{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'0', 'a'}, {'0', 'b'}, 
+{'0', 'c'}, {'0', 'd'}, {'0', 'e'}, {'0', 'f'}, {'1', '0'}, {'1', '1'}, 
+{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, 
+{'1', '8'}, {'1', '9'}, {'1', 'a'}, {'1', 'b'}, {'1', 'c'}, {'1', 'd'}, 
+{'1', 'e'}, {'1', 'f'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, 
+{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, 
+{'2', 'a'}, {'2', 'b'}, {'2', 'c'}, {'2', 'd'}, {'2', 'e'}, {'2', 'f'}, 
+{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, 
+{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'3', 'a'}, {'3', 'b'}, 
+{'3', 'c'}, {'3', 'd'}, {'3', 'e'}, {'3', 'f'}, {'4', '0'}, {'4', '1'}, 
+{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, 
+{'4', '8'}, {'4', '9'}, {'4', 'a'}, {'4', 'b'}, {'4', 'c'}, {'4', 'd'}, 
+{'4', 'e'}, {'4', 'f'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, 
+{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, 
+{'5', 'a'}, {'5', 'b'}, {'5', 'c'}, {'5', 'd'}, {'5', 'e'}, {'5', 'f'}, 
+{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, 
+{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'6', 'a'}, {'6', 'b'}, 
+{'6', 'c'}, {'6', 'd'}, {'6', 'e'}, {'6', 'f'}, {'7', '0'}, {'7', '1'}, 
+{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, 
+{'7', '8'}, {'7', '9'}, {'7', 'a'}, {'7', 'b'}, {'7', 'c'}, {'7', 'd'}, 
+{'7', 'e'}, {'7', 'f'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, 
+{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, 
+{'8', 'a'}, {'8', 'b'}, {'8', 'c'}, {'8', 'd'}, {'8', 'e'}, {'8', 'f'}, 
+{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, 
+{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}, {'9', 'a'}, {'9', 'b'}, 
+{'9', 'c'}, {'9', 'd'}, {'9', 'e'}, {'9', 'f'}, {'a', '0'}, {'a', '1'}, 
+{'a', '2'}, {'a', '3'}, {'a', '4'}, {'a', '5'}, {'a', '6'}, {'a', '7'}, 
+{'a', '8'}, {'a', '9'}, {'a', 'a'}, {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, 
+{'a', 'e'}, {'a', 'f'}, {'b', '0'}, {'b', '1'}, {'b', '2'}, {'b', '3'}, 
+{'b', '4'}, {'b', '5'}, {'b', '6'}, {'b', '7'}, {'b', '8'}, {'b', '9'}, 
+{'b', 'a'}, {'b', 'b'}, {'b', 'c'}, {'b', 'd'}, {'b', 'e'}, {'b', 'f'}, 
+{'c', '0'}, {'c', '1'}, {'c', '2'}, {'c', '3'}, {'c', '4'}, {'c', '5'}, 
+{'c', '6'}, {'c', '7'}, {'c', '8'}, {'c', '9'}, {'c', 'a'}, {'c', 'b'}, 
+{'c', 'c'}, {'c', 'd'}, {'c', 'e'}, {'c', 'f'}, {'d', '0'}, {'d', '1'}, 
+{'d', '2'}, {'d', '3'}, {'d', '4'}, {'d', '5'}, {'d', '6'}, {'d', '7'}, 
+{'d', '8'}, {'d', '9'}, {'d', 'a'}, {'d', 'b'}, {'d', 'c'}, {'d', 'd'}, 
+{'d', 'e'}, {'d', 'f'}, {'e', '0'}, {'e', '1'}, {'e', '2'}, {'e', '3'}, 
+{'e', '4'}, {'e', '5'}, {'e', '6'}, {'e', '7'}, {'e', '8'}, {'e', '9'}, 
+{'e', 'a'}, {'e', 'b'}, {'e', 'c'}, {'e', 'd'}, {'e', 'e'}, {'e', 'f'}, 
+{'f', '0'}, {'f', '1'}, {'f', '2'}, {'f', '3'}, {'f', '4'}, {'f', '5'}, 
+{'f', '6'}, {'f', '7'}, {'f', '8'}, {'f', '9'}, {'f', 'a'}, {'f', 'b'}, 
+{'f', 'c'}, {'f', 'd'}, {'f', 'e'}, {'f', 'f'}, 
+};
+
+extern const char formatHexUpper[256][2] = {
+{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, 
+{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'0', 'A'}, {'0', 'B'}, 
+{'0', 'C'}, {'0', 'D'}, {'0', 'E'}, {'0', 'F'}, {'1', '0'}, {'1', '1'}, 
+{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, 
+{'1', '8'}, {'1', '9'}, {'1', 'A'}, {'1', 'B'}, {'1', 'C'}, {'1', 'D'}, 
+{'1', 'E'}, {'1', 'F'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, 
+{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, 
+{'2', 'A'}, {'2', 'B'}, {'2', 'C'}, {'2', 'D'}, {'2', 'E'}, {'2', 'F'}, 
+{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, 
+{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'3', 'A'}, {'3', 'B'}, 
+{'3', 'C'}, {'3', 'D'}, {'3', 'E'}, {'3', 'F'}, {'4', '0'}, {'4', '1'}, 
+{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, 
+{'4', '8'}, {'4', '9'}, {'4', 'A'}, {'4', 'B'}, {'4', 'C'}, {'4', 'D'}, 
+{'4', 'E'}, {'4', 'F'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, 
+{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, 
+{'5', 'A'}, {'5', 'B'}, {'5', 'C'}, {'5', 'D'}, {'5', 'E'}, {'5', 'F'}, 
+{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, 
+{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'6', 'A'}, {'6', 'B'}, 
+{'6', 'C'}, {'6', 'D'}, {'6', 'E'}, {'6', 'F'}, {'7', '0'}, {'7', '1'}, 
+{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, 
+{'7', '8'}, {'7', '9'}, {'7', 'A'}, {'7', 'B'}, {'7', 'C'}, {'7', 'D'}, 
+{'7', 'E'}, {'7', 'F'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, 
+{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, 
+{'8', 'A'}, {'8', 'B'}, {'8', 'C'}, {'8', 'D'}, {'8', 'E'}, {'8', 'F'}, 
+{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, 
+{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}, {'9', 'A'}, {'9', 'B'}, 
+{'9', 'C'}, {'9', 'D'}, {'9', 'E'}, {'9', 'F'}, {'A', '0'}, {'A', '1'}, 
+{'A', '2'}, {'A', '3'}, {'A', '4'}, {'A', '5'}, {'A', '6'}, {'A', '7'}, 
+{'A', '8'}, {'A', '9'}, {'A', 'A'}, {'A', 'B'}, {'A', 'C'}, {'A', 'D'}, 
+{'A', 'E'}, {'A', 'F'}, {'B', '0'}, {'B', '1'}, {'B', '2'}, {'B', '3'}, 
+{'B', '4'}, {'B', '5'}, {'B', '6'}, {'B', '7'}, {'B', '8'}, {'B', '9'}, 
+{'B', 'A'}, {'B', 'B'}, {'B', 'C'}, {'B', 'D'}, {'B', 'E'}, {'B', 'F'}, 
+{'C', '0'}, {'C', '1'}, {'C', '2'}, {'C', '3'}, {'C', '4'}, {'C', '5'}, 
+{'C', '6'}, {'C', '7'}, {'C', '8'}, {'C', '9'}, {'C', 'A'}, {'C', 'B'}, 
+{'C', 'C'}, {'C', 'D'}, {'C', 'E'}, {'C', 'F'}, {'D', '0'}, {'D', '1'}, 
+{'D', '2'}, {'D', '3'}, {'D', '4'}, {'D', '5'}, {'D', '6'}, {'D', '7'}, 
+{'D', '8'}, {'D', '9'}, {'D', 'A'}, {'D', 'B'}, {'D', 'C'}, {'D', 'D'}, 
+{'D', 'E'}, {'D', 'F'}, {'E', '0'}, {'E', '1'}, {'E', '2'}, {'E', '3'}, 
+{'E', '4'}, {'E', '5'}, {'E', '6'}, {'E', '7'}, {'E', '8'}, {'E', '9'}, 
+{'E', 'A'}, {'E', 'B'}, {'E', 'C'}, {'E', 'D'}, {'E', 'E'}, {'E', 'F'}, 
+{'F', '0'}, {'F', '1'}, {'F', '2'}, {'F', '3'}, {'F', '4'}, {'F', '5'}, 
+{'F', '6'}, {'F', '7'}, {'F', '8'}, {'F', '9'}, {'F', 'A'}, {'F', 'B'}, 
+{'F', 'C'}, {'F', 'D'}, {'F', 'E'}, {'F', 'F'}, 
+};
+
+extern const char formatBinary[256][8] = {
+{'0', '0', '0', '0', '0', '0', '0', '0'}, 
+{'0', '0', '0', '0', '0', '0', '0', '1'}, 
+{'0', '0', '0', '0', '0', '0', '1', '0'}, 
+{'0', '0', '0', '0', '0', '0', '1', '1'}, 
+{'0', '0', '0', '0', '0', '1', '0', '0'}, 
+{'0', '0', '0', '0', '0', '1', '0', '1'}, 
+{'0', '0', '0', '0', '0', '1', '1', '0'}, 
+{'0', '0', '0', '0', '0', '1', '1', '1'}, 
+{'0', '0', '0', '0', '1', '0', '0', '0'}, 
+{'0', '0', '0', '0', '1', '0', '0', '1'}, 
+{'0', '0', '0', '0', '1', '0', '1', '0'}, 
+{'0', '0', '0', '0', '1', '0', '1', '1'}, 
+{'0', '0', '0', '0', '1', '1', '0', '0'}, 
+{'0', '0', '0', '0', '1', '1', '0', '1'}, 
+{'0', '0', '0', '0', '1', '1', '1', '0'}, 
+{'0', '0', '0', '0', '1', '1', '1', '1'}, 
+{'0', '0', '0', '1', '0', '0', '0', '0'}, 
+{'0', '0', '0', '1', '0', '0', '0', '1'}, 
+{'0', '0', '0', '1', '0', '0', '1', '0'}, 
+{'0', '0', '0', '1', '0', '0', '1', '1'}, 
+{'0', '0', '0', '1', '0', '1', '0', '0'}, 
+{'0', '0', '0', '1', '0', '1', '0', '1'}, 
+{'0', '0', '0', '1', '0', '1', '1', '0'}, 
+{'0', '0', '0', '1', '0', '1', '1', '1'}, 
+{'0', '0', '0', '1', '1', '0', '0', '0'}, 
+{'0', '0', '0', '1', '1', '0', '0', '1'}, 
+{'0', '0', '0', '1', '1', '0', '1', '0'}, 
+{'0', '0', '0', '1', '1', '0', '1', '1'}, 
+{'0', '0', '0', '1', '1', '1', '0', '0'}, 
+{'0', '0', '0', '1', '1', '1', '0', '1'}, 
+{'0', '0', '0', '1', '1', '1', '1', '0'}, 
+{'0', '0', '0', '1', '1', '1', '1', '1'}, 
+{'0', '0', '1', '0', '0', '0', '0', '0'}, 
+{'0', '0', '1', '0', '0', '0', '0', '1'}, 
+{'0', '0', '1', '0', '0', '0', '1', '0'}, 
+{'0', '0', '1', '0', '0', '0', '1', '1'}, 
+{'0', '0', '1', '0', '0', '1', '0', '0'}, 
+{'0', '0', '1', '0', '0', '1', '0', '1'}, 
+{'0', '0', '1', '0', '0', '1', '1', '0'}, 
+{'0', '0', '1', '0', '0', '1', '1', '1'}, 
+{'0', '0', '1', '0', '1', '0', '0', '0'}, 
+{'0', '0', '1', '0', '1', '0', '0', '1'}, 
+{'0', '0', '1', '0', '1', '0', '1', '0'}, 
+{'0', '0', '1', '0', '1', '0', '1', '1'}, 
+{'0', '0', '1', '0', '1', '1', '0', '0'}, 
+{'0', '0', '1', '0', '1', '1', '0', '1'}, 
+{'0', '0', '1', '0', '1', '1', '1', '0'}, 
+{'0', '0', '1', '0', '1', '1', '1', '1'}, 
+{'0', '0', '1', '1', '0', '0', '0', '0'}, 
+{'0', '0', '1', '1', '0', '0', '0', '1'}, 
+{'0', '0', '1', '1', '0', '0', '1', '0'}, 
+{'0', '0', '1', '1', '0', '0', '1', '1'}, 
+{'0', '0', '1', '1', '0', '1', '0', '0'}, 
+{'0', '0', '1', '1', '0', '1', '0', '1'}, 
+{'0', '0', '1', '1', '0', '1', '1', '0'}, 
+{'0', '0', '1', '1', '0', '1', '1', '1'}, 
+{'0', '0', '1', '1', '1', '0', '0', '0'}, 
+{'0', '0', '1', '1', '1', '0', '0', '1'}, 
+{'0', '0', '1', '1', '1', '0', '1', '0'}, 
+{'0', '0', '1', '1', '1', '0', '1', '1'}, 
+{'0', '0', '1', '1', '1', '1', '0', '0'}, 
+{'0', '0', '1', '1', '1', '1', '0', '1'}, 
+{'0', '0', '1', '1', '1', '1', '1', '0'}, 
+{'0', '0', '1', '1', '1', '1', '1', '1'}, 
+{'0', '1', '0', '0', '0', '0', '0', '0'}, 
+{'0', '1', '0', '0', '0', '0', '0', '1'}, 
+{'0', '1', '0', '0', '0', '0', '1', '0'}, 
+{'0', '1', '0', '0', '0', '0', '1', '1'}, 
+{'0', '1', '0', '0', '0', '1', '0', '0'}, 
+{'0', '1', '0', '0', '0', '1', '0', '1'}, 
+{'0', '1', '0', '0', '0', '1', '1', '0'}, 
+{'0', '1', '0', '0', '0', '1', '1', '1'}, 
+{'0', '1', '0', '0', '1', '0', '0', '0'}, 
+{'0', '1', '0', '0', '1', '0', '0', '1'}, 
+{'0', '1', '0', '0', '1', '0', '1', '0'}, 
+{'0', '1', '0', '0', '1', '0', '1', '1'}, 
+{'0', '1', '0', '0', '1', '1', '0', '0'}, 
+{'0', '1', '0', '0', '1', '1', '0', '1'}, 
+{'0', '1', '0', '0', '1', '1', '1', '0'}, 
+{'0', '1', '0', '0', '1', '1', '1', '1'}, 
+{'0', '1', '0', '1', '0', '0', '0', '0'}, 
+{'0', '1', '0', '1', '0', '0', '0', '1'}, 
+{'0', '1', '0', '1', '0', '0', '1', '0'}, 
+{'0', '1', '0', '1', '0', '0', '1', '1'}, 
+{'0', '1', '0', '1', '0', '1', '0', '0'}, 
+{'0', '1', '0', '1', '0', '1', '0', '1'}, 
+{'0', '1', '0', '1', '0', '1', '1', '0'}, 
+{'0', '1', '0', '1', '0', '1', '1', '1'}, 
+{'0', '1', '0', '1', '1', '0', '0', '0'}, 
+{'0', '1', '0', '1', '1', '0', '0', '1'}, 
+{'0', '1', '0', '1', '1', '0', '1', '0'}, 
+{'0', '1', '0', '1', '1', '0', '1', '1'}, 
+{'0', '1', '0', '1', '1', '1', '0', '0'}, 
+{'0', '1', '0', '1', '1', '1', '0', '1'}, 
+{'0', '1', '0', '1', '1', '1', '1', '0'}, 
+{'0', '1', '0', '1', '1', '1', '1', '1'}, 
+{'0', '1', '1', '0', '0', '0', '0', '0'}, 
+{'0', '1', '1', '0', '0', '0', '0', '1'}, 
+{'0', '1', '1', '0', '0', '0', '1', '0'}, 
+{'0', '1', '1', '0', '0', '0', '1', '1'}, 
+{'0', '1', '1', '0', '0', '1', '0', '0'}, 
+{'0', '1', '1', '0', '0', '1', '0', '1'}, 
+{'0', '1', '1', '0', '0', '1', '1', '0'}, 
+{'0', '1', '1', '0', '0', '1', '1', '1'}, 
+{'0', '1', '1', '0', '1', '0', '0', '0'}, 
+{'0', '1', '1', '0', '1', '0', '0', '1'}, 
+{'0', '1', '1', '0', '1', '0', '1', '0'}, 
+{'0', '1', '1', '0', '1', '0', '1', '1'}, 
+{'0', '1', '1', '0', '1', '1', '0', '0'}, 
+{'0', '1', '1', '0', '1', '1', '0', '1'}, 
+{'0', '1', '1', '0', '1', '1', '1', '0'}, 
+{'0', '1', '1', '0', '1', '1', '1', '1'}, 
+{'0', '1', '1', '1', '0', '0', '0', '0'}, 
+{'0', '1', '1', '1', '0', '0', '0', '1'}, 
+{'0', '1', '1', '1', '0', '0', '1', '0'}, 
+{'0', '1', '1', '1', '0', '0', '1', '1'}, 
+{'0', '1', '1', '1', '0', '1', '0', '0'}, 
+{'0', '1', '1', '1', '0', '1', '0', '1'}, 
+{'0', '1', '1', '1', '0', '1', '1', '0'}, 
+{'0', '1', '1', '1', '0', '1', '1', '1'}, 
+{'0', '1', '1', '1', '1', '0', '0', '0'}, 
+{'0', '1', '1', '1', '1', '0', '0', '1'}, 
+{'0', '1', '1', '1', '1', '0', '1', '0'}, 
+{'0', '1', '1', '1', '1', '0', '1', '1'}, 
+{'0', '1', '1', '1', '1', '1', '0', '0'}, 
+{'0', '1', '1', '1', '1', '1', '0', '1'}, 
+{'0', '1', '1', '1', '1', '1', '1', '0'}, 
+{'0', '1', '1', '1', '1', '1', '1', '1'}, 
+{'1', '0', '0', '0', '0', '0', '0', '0'}, 
+{'1', '0', '0', '0', '0', '0', '0', '1'}, 
+{'1', '0', '0', '0', '0', '0', '1', '0'}, 
+{'1', '0', '0', '0', '0', '0', '1', '1'}, 
+{'1', '0', '0', '0', '0', '1', '0', '0'}, 
+{'1', '0', '0', '0', '0', '1', '0', '1'}, 
+{'1', '0', '0', '0', '0', '1', '1', '0'}, 
+{'1', '0', '0', '0', '0', '1', '1', '1'}, 
+{'1', '0', '0', '0', '1', '0', '0', '0'}, 
+{'1', '0', '0', '0', '1', '0', '0', '1'}, 
+{'1', '0', '0', '0', '1', '0', '1', '0'}, 
+{'1', '0', '0', '0', '1', '0', '1', '1'}, 
+{'1', '0', '0', '0', '1', '1', '0', '0'}, 
+{'1', '0', '0', '0', '1', '1', '0', '1'}, 
+{'1', '0', '0', '0', '1', '1', '1', '0'}, 
+{'1', '0', '0', '0', '1', '1', '1', '1'}, 
+{'1', '0', '0', '1', '0', '0', '0', '0'}, 
+{'1', '0', '0', '1', '0', '0', '0', '1'}, 
+{'1', '0', '0', '1', '0', '0', '1', '0'}, 
+{'1', '0', '0', '1', '0', '0', '1', '1'}, 
+{'1', '0', '0', '1', '0', '1', '0', '0'}, 
+{'1', '0', '0', '1', '0', '1', '0', '1'}, 
+{'1', '0', '0', '1', '0', '1', '1', '0'}, 
+{'1', '0', '0', '1', '0', '1', '1', '1'}, 
+{'1', '0', '0', '1', '1', '0', '0', '0'}, 
+{'1', '0', '0', '1', '1', '0', '0', '1'}, 
+{'1', '0', '0', '1', '1', '0', '1', '0'}, 
+{'1', '0', '0', '1', '1', '0', '1', '1'}, 
+{'1', '0', '0', '1', '1', '1', '0', '0'}, 
+{'1', '0', '0', '1', '1', '1', '0', '1'}, 
+{'1', '0', '0', '1', '1', '1', '1', '0'}, 
+{'1', '0', '0', '1', '1', '1', '1', '1'}, 
+{'1', '0', '1', '0', '0', '0', '0', '0'}, 
+{'1', '0', '1', '0', '0', '0', '0', '1'}, 
+{'1', '0', '1', '0', '0', '0', '1', '0'}, 
+{'1', '0', '1', '0', '0', '0', '1', '1'}, 
+{'1', '0', '1', '0', '0', '1', '0', '0'}, 
+{'1', '0', '1', '0', '0', '1', '0', '1'}, 
+{'1', '0', '1', '0', '0', '1', '1', '0'}, 
+{'1', '0', '1', '0', '0', '1', '1', '1'}, 
+{'1', '0', '1', '0', '1', '0', '0', '0'}, 
+{'1', '0', '1', '0', '1', '0', '0', '1'}, 
+{'1', '0', '1', '0', '1', '0', '1', '0'}, 
+{'1', '0', '1', '0', '1', '0', '1', '1'}, 
+{'1', '0', '1', '0', '1', '1', '0', '0'}, 
+{'1', '0', '1', '0', '1', '1', '0', '1'}, 
+{'1', '0', '1', '0', '1', '1', '1', '0'}, 
+{'1', '0', '1', '0', '1', '1', '1', '1'}, 
+{'1', '0', '1', '1', '0', '0', '0', '0'}, 
+{'1', '0', '1', '1', '0', '0', '0', '1'}, 
+{'1', '0', '1', '1', '0', '0', '1', '0'}, 
+{'1', '0', '1', '1', '0', '0', '1', '1'}, 
+{'1', '0', '1', '1', '0', '1', '0', '0'}, 
+{'1', '0', '1', '1', '0', '1', '0', '1'}, 
+{'1', '0', '1', '1', '0', '1', '1', '0'}, 
+{'1', '0', '1', '1', '0', '1', '1', '1'}, 
+{'1', '0', '1', '1', '1', '0', '0', '0'}, 
+{'1', '0', '1', '1', '1', '0', '0', '1'}, 
+{'1', '0', '1', '1', '1', '0', '1', '0'}, 
+{'1', '0', '1', '1', '1', '0', '1', '1'}, 
+{'1', '0', '1', '1', '1', '1', '0', '0'}, 
+{'1', '0', '1', '1', '1', '1', '0', '1'}, 
+{'1', '0', '1', '1', '1', '1', '1', '0'}, 
+{'1', '0', '1', '1', '1', '1', '1', '1'}, 
+{'1', '1', '0', '0', '0', '0', '0', '0'}, 
+{'1', '1', '0', '0', '0', '0', '0', '1'}, 
+{'1', '1', '0', '0', '0', '0', '1', '0'}, 
+{'1', '1', '0', '0', '0', '0', '1', '1'}, 
+{'1', '1', '0', '0', '0', '1', '0', '0'}, 
+{'1', '1', '0', '0', '0', '1', '0', '1'}, 
+{'1', '1', '0', '0', '0', '1', '1', '0'}, 
+{'1', '1', '0', '0', '0', '1', '1', '1'}, 
+{'1', '1', '0', '0', '1', '0', '0', '0'}, 
+{'1', '1', '0', '0', '1', '0', '0', '1'}, 
+{'1', '1', '0', '0', '1', '0', '1', '0'}, 
+{'1', '1', '0', '0', '1', '0', '1', '1'}, 
+{'1', '1', '0', '0', '1', '1', '0', '0'}, 
+{'1', '1', '0', '0', '1', '1', '0', '1'}, 
+{'1', '1', '0', '0', '1', '1', '1', '0'}, 
+{'1', '1', '0', '0', '1', '1', '1', '1'}, 
+{'1', '1', '0', '1', '0', '0', '0', '0'}, 
+{'1', '1', '0', '1', '0', '0', '0', '1'}, 
+{'1', '1', '0', '1', '0', '0', '1', '0'}, 
+{'1', '1', '0', '1', '0', '0', '1', '1'}, 
+{'1', '1', '0', '1', '0', '1', '0', '0'}, 
+{'1', '1', '0', '1', '0', '1', '0', '1'}, 
+{'1', '1', '0', '1', '0', '1', '1', '0'}, 
+{'1', '1', '0', '1', '0', '1', '1', '1'}, 
+{'1', '1', '0', '1', '1', '0', '0', '0'}, 
+{'1', '1', '0', '1', '1', '0', '0', '1'}, 
+{'1', '1', '0', '1', '1', '0', '1', '0'}, 
+{'1', '1', '0', '1', '1', '0', '1', '1'}, 
+{'1', '1', '0', '1', '1', '1', '0', '0'}, 
+{'1', '1', '0', '1', '1', '1', '0', '1'}, 
+{'1', '1', '0', '1', '1', '1', '1', '0'}, 
+{'1', '1', '0', '1', '1', '1', '1', '1'}, 
+{'1', '1', '1', '0', '0', '0', '0', '0'}, 
+{'1', '1', '1', '0', '0', '0', '0', '1'}, 
+{'1', '1', '1', '0', '0', '0', '1', '0'}, 
+{'1', '1', '1', '0', '0', '0', '1', '1'}, 
+{'1', '1', '1', '0', '0', '1', '0', '0'}, 
+{'1', '1', '1', '0', '0', '1', '0', '1'}, 
+{'1', '1', '1', '0', '0', '1', '1', '0'}, 
+{'1', '1', '1', '0', '0', '1', '1', '1'}, 
+{'1', '1', '1', '0', '1', '0', '0', '0'}, 
+{'1', '1', '1', '0', '1', '0', '0', '1'}, 
+{'1', '1', '1', '0', '1', '0', '1', '0'}, 
+{'1', '1', '1', '0', '1', '0', '1', '1'}, 
+{'1', '1', '1', '0', '1', '1', '0', '0'}, 
+{'1', '1', '1', '0', '1', '1', '0', '1'}, 
+{'1', '1', '1', '0', '1', '1', '1', '0'}, 
+{'1', '1', '1', '0', '1', '1', '1', '1'}, 
+{'1', '1', '1', '1', '0', '0', '0', '0'}, 
+{'1', '1', '1', '1', '0', '0', '0', '1'}, 
+{'1', '1', '1', '1', '0', '0', '1', '0'}, 
+{'1', '1', '1', '1', '0', '0', '1', '1'}, 
+{'1', '1', '1', '1', '0', '1', '0', '0'}, 
+{'1', '1', '1', '1', '0', '1', '0', '1'}, 
+{'1', '1', '1', '1', '0', '1', '1', '0'}, 
+{'1', '1', '1', '1', '0', '1', '1', '1'}, 
+{'1', '1', '1', '1', '1', '0', '0', '0'}, 
+{'1', '1', '1', '1', '1', '0', '0', '1'}, 
+{'1', '1', '1', '1', '1', '0', '1', '0'}, 
+{'1', '1', '1', '1', '1', '0', '1', '1'}, 
+{'1', '1', '1', '1', '1', '1', '0', '0'}, 
+{'1', '1', '1', '1', '1', '1', '0', '1'}, 
+{'1', '1', '1', '1', '1', '1', '1', '0'}, 
+{'1', '1', '1', '1', '1', '1', '1', '1'}, 
+};
+
+}  // namespace detail
+}  // namespace folly
diff --git a/faux-folly/folly/android/test/faux-folly-tests.sh b/faux-folly/folly/android/test/faux-folly-tests.sh
new file mode 100755
index 0000000..f26c3ba
--- /dev/null
+++ b/faux-folly/folly/android/test/faux-folly-tests.sh
@@ -0,0 +1,177 @@
+#!/system/bin/sh
+#
+# Copyright 2015 Nest Labs, Inc. All rights reserved.
+
+if [ -z "$PACKAGE" ] ; then
+    PACKAGE="faux-folly-tests"
+fi
+if [ -z "$TEST_ROOT" ] ; then
+    TEST_ROOT="$(dirname $0)"
+    grep -vE "^/" "$TEST_ROOT" &> /dev/null
+    if [ $? -eq 0 ] ; then
+        TEST_ROOT="$(pwd)/${TEST_ROOT}"
+    fi
+fi
+THIS_SCRIPT="$(basename $0)"
+
+###
+### GLOBAL VARIABLES
+###
+### only export variables that mutate
+###
+
+# Increment when a test passes
+export GLOBAL_PASS=0
+# Increment when a test fails
+export GLOBAL_FAIL=0
+# The directory where helper test programs are installed
+TEST_BIN="${TEST_ROOT}/bin"
+# The directory where helper test data is installed
+TEST_DATA="${TEST_ROOT}/data"
+# The temporary directory where raw program output is placed
+TMPDIR=/storage
+# The log file with all raw program output
+LOGFILE=$(mktemp --tmpdir="${TMPDIR}" ${PACKAGE}-log.XXXXXXXX)
+# A temporary file, usually used for staging raw program output for grep
+TMPFILE=$(mktemp --tmpdir="${TMPDIR}" ${PACKAGE}-tmp.XXXXXXXX)
+
+# The scripts need their PATH set, too
+export PATH="${TEST_BIN}:${PATH}"
+
+# run_test
+#
+# This is a /bin/sh port of add_gflags_test that is implemented by
+# cmake/utils.cmake and cmake/execute_test.cmake
+#
+# Usage: run_test <test-name> <expected-return-code> <expected-text-output> <unexpected-text-output> <cmd> [args...]
+#
+# test-name: A symbolic name for the test for logging
+#
+# expected-return-code: the number passed back to the operating system. default: 0
+# if a blank string is given.
+#
+# expected-text-output: if a non-empty string, it is expected that the command will
+# output this string. It must be seen in order to pass.
+#
+# unexpected-text-output: if a non-empty string, it is expected that the command
+# will NOT output this string. If it is seen, the test will fail.
+#
+# cmd: executable to run (full path or in $PATH)
+#
+# args: all arguments that need to be passed on to the executable.
+#
+run_test()
+{
+    TEST_NAME="$1"
+    shift
+    EXPECTED_RC="$1"
+    shift
+    EXPECTED_OUTPUT="$1"
+    shift
+    UNEXPECTED_OUTPUT="$1"
+    shift
+
+    if [ $# -eq 0 ] ; then
+        GLOBAL_RETURN_CODE=1
+        echo "FATAL ERROR: invalid arguments passed to run_test for test='$TEST_NAME'"
+        return 1
+    fi
+
+    if [ -z "$EXPECTED_RC" ] ; then
+        EXPECTED_RC="0"
+    fi
+
+    VERDICT="pass"
+    REASON="default"
+
+    echo "RUNNING TEST '${TEST_NAME}' >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" > "$TMPFILE"
+    echo "CMDLINE: $@" >> "$TMPFILE"
+    "$@" &>> "$TMPFILE"
+    RC=$?
+    echo "RC=$RC" >> "$TMPFILE"
+    echo "END OF TEST '${TEST_NAME}' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> "$TMPFILE"
+
+    EXPECTED_FOUND=1
+    UNEXPECTED_FOUND=0
+
+    if [ -n "$EXPECTED_OUTPUT" ] ; then
+        grep -- "$EXPECTED_OUTPUT" "$TMPFILE" &> /dev/null
+        if [ $? -ne 0 ] ; then
+            EXPECTED_FOUND=0
+        fi
+    fi
+
+    if [ -n "$UNEXPECTED_OUTPUT" ] ; then
+        grep -- "$UNEXPECTED_OUTPUT" "$TMPFILE" &> /dev/null
+        if [ $? -eq 0 ] ; then
+            UNEXPECTED_FOUND=1
+        fi
+    fi
+
+    if [ "$RC" != "$EXPECTED_RC" ] ; then
+        VERDICT="fail"
+        REASON="unexpected return code (got ${RC}, expected ${EXPECTED_RC})"
+    elif [ "$EXPECTED_FOUND" = "0" ] ; then
+        VERDICT="fail"
+        REASON="missing expected output, expected '${EXPECTED_OUTPUT}', see '$LOGFILE'"
+    elif [ "$UNEXPECTED_FOUND" = "1" ] ; then
+        VERDICT="fail"
+        REASON="Unexpected output detected: '${UNEXPECTED_OUTPUT}', see '$LOGFILE'"
+    fi
+
+    cat "$TMPFILE" >> "$LOGFILE"
+
+    if [ "$VERDICT" = "pass" ] ; then
+        echo "${TEST_NAME}: pass"
+        GLOBAL_PASS=$(($GLOBAL_PASS + 1))
+        return 0
+    else
+        echo "${TEST_NAME}: fail ($REASON)"
+        GLOBAL_FAIL=$(($GLOBAL_FAIL + 1))
+        return 1
+    fi
+}
+
+# Tests must be executed from here:
+cd "$TEST_ROOT"
+
+###
+### TEST CASES
+###
+
+# Test this script
+run_test "the_truth" 0 "" "" true
+run_test "the_lie" 1 "" "" false
+run_test "the_word" "" "really unlikely" "" echo "really unlikely"
+
+for T in "${TEST_BIN}"/* ; do
+    NAME=$(basename "${T}")
+    if [ "x${NAME}" == "x${THIS_SCRIPT}" ] ; then
+        continue
+    fi
+    if [ ! -x "${T}" ] ; then
+	echo "Skipping '${NAME}' because it doesn't have execute permission"
+	continue
+    fi
+    if [ ! -f "${T}" ] ; then
+	echo "Skipping '${NAME}' because it is not a file"
+	continue
+    fi
+    run_test "$NAME" 0 "" "" "$T"
+done
+
+###
+### RESULTS
+###
+
+if [ $GLOBAL_FAIL -ne 0 ] ; then
+    RETURN_CODE=1
+else
+    RETURN_CODE=0
+fi
+TOTAL_TESTS=$(($GLOBAL_PASS + $GLOBAL_FAIL))
+
+echo "${GLOBAL_PASS}/${TOTAL_TESTS} passed. See '$LOGFILE'"
+rm "$TMPFILE"
+
+exit $RETURN_CODE
diff --git a/faux-folly/folly/build/bootstrap-osx-homebrew.sh b/faux-folly/folly/build/bootstrap-osx-homebrew.sh
new file mode 100755
index 0000000..c7c350c
--- /dev/null
+++ b/faux-folly/folly/build/bootstrap-osx-homebrew.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -x
+# The only prerequisite should be homebrew. If something doesn't work out of
+# the box with just homebrew, let's fix it.
+
+# fail fast
+set -e
+
+BASE_DIR="$(cd "$(dirname -- "$0")"/.. ; pwd)"  # folly/folly
+cd "$BASE_DIR"
+
+brewget() {
+    brew install $@ || brew upgrade $@
+}
+
+# tool dependencies: autotools and scons (for double-conversion)
+brewget autoconf automake libtool
+
+# dependencies
+brewget glog gflags boost libevent double-conversion
+
+autoreconf -i
+./configure
+
+pushd test
+test -e gtest-1.7.0.zip || {
+    curl -O https://googletest.googlecode.com/files/gtest-1.7.0.zip
+    unzip gtest-1.7.0.zip
+}
+popd
diff --git a/faux-folly/folly/build/deps_ubuntu_12.04.sh b/faux-folly/folly/build/deps_ubuntu_12.04.sh
new file mode 100755
index 0000000..4e3e7d2
--- /dev/null
+++ b/faux-folly/folly/build/deps_ubuntu_12.04.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+# Installs folly's dependencies to /usr/local on a clean Ubuntu 12.04 x64
+# system.  Primarily intended for Travis CI, since most engineers don't run
+# distributions this stale.
+#
+# WARNING: Uses 'sudo' to upgrade your system with impunity:
+#  - Adds several PPAs for missing/outdated dependencies
+#  - Installs several from-source dependencies in /usr/local
+#
+# Library sources & build files end up in folly/folly/deps/
+
+set -ex
+
+BUILD_DIR="$(readlink -f "$(dirname "$0")")"
+mkdir -p "$BUILD_DIR/deps"
+cd "$BUILD_DIR/deps"
+
+sudo apt-get install -y python-software-properties  # for add-apt-repository
+sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+sudo add-apt-repository -y ppa:boost-latest/ppa
+sudo apt-get update
+
+sudo apt-get install -y git gcc-4.8 g++-4.8 libboost1.54-dev autoconf git \
+  libboost-thread1.54-dev libboost-filesystem1.54-dev libssl-dev cmake \
+  libsnappy-dev libboost-system1.54-dev libboost-regex1.54-dev make \
+  libboost-context1.54-dev libtool libevent-dev libgtest-dev binutils-dev
+
+# TODO: According to the folly docs, these system dependencies might be
+# missing.  However, things seem to build fine...
+#  automake autoconf-archive libboost-all-dev liblz4-dev liblzma-dev
+#  zlib1g-dev libjemalloc-dev
+
+sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50
+sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50
+
+CMAKE_NAME=cmake-2.8.12.1
+GFLAGS_VER=2.1.1
+GLOG_NAME=glog-0.3.3
+
+# double-conversion
+pushd .
+git clone https://github.com/google/double-conversion
+cd double-conversion
+cmake -DBUILD_SHARED_LIBS=ON .  # Don't use scons instead, it's broken.
+make
+sudo make install
+sudo ldconfig
+popd
+
+# Newer cmake, since the system's 2.8.7 cmake is too old for gflags:
+# https://groups.google.com/forum/#!topic/google-gflags/bu1iIDKn-ok
+pushd .
+wget http://www.cmake.org/files/v2.8/${CMAKE_NAME}.tar.gz \
+  -O ${CMAKE_NAME}.tar.gz
+tar xzf ${CMAKE_NAME}.tar.gz
+cd ${CMAKE_NAME}
+cmake .
+make
+CMAKE="$(readlink -f bin/cmake)"
+popd
+
+# gflags
+pushd .
+wget https://github.com/gflags/gflags/archive/v${GFLAGS_VER}.tar.gz \
+  -O gflags-${GFLAGS_VER}.tar.gz
+tar xzf gflags-${GFLAGS_VER}.tar.gz
+mkdir -p gflags-${GFLAGS_VER}/build/ && cd gflags-${GFLAGS_VER}/build/
+"$CMAKE" .. -DBUILD_SHARED_LIBS:BOOL=ON -DGFLAGS_NAMESPACE:STRING=google
+make
+sudo make install
+sudo ldconfig
+popd
+
+# glog
+pushd .
+wget https://google-glog.googlecode.com/files/${GLOG_NAME}.tar.gz \
+  -O ${GLOG_NAME}.tar.gz
+tar xzf ${GLOG_NAME}.tar.gz
+cd ${GLOG_NAME}
+./configure
+make
+sudo make install
+sudo ldconfig
+popd
diff --git a/faux-folly/folly/build/generate_escape_tables.py b/faux-folly/folly/build/generate_escape_tables.py
new file mode 100755
index 0000000..8ca67bd
--- /dev/null
+++ b/faux-folly/folly/build/generate_escape_tables.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+#
+# Generate Escape tables.
+# Copyright 2011 Facebook
+#
+# @author Tudor Bosman (tudorb@fb.com)
+#
+import os
+from optparse import OptionParser
+
+OUTPUT_FILE = "EscapeTables.cpp"
+
+def generate(f):
+    f.write("namespace folly {\n"
+            "namespace detail {\n"
+            "\n")
+
+    f.write("extern const char cEscapeTable[] =\n")
+    escapes = dict((
+        ('"', '\\"'),
+        ('\\', '\\\\'),
+        ('?', '?'),
+        ('\n', 'n'),
+        ('\r', 'r'),
+        ('\t', 't'),
+    ))
+    for i in range(0, 256):
+        if i % 64 == 0:
+            if i != 0:
+                f.write("\"\n")
+            f.write("  \"")
+        c = chr(i)
+        if c in escapes:
+            c = escapes[c]
+        elif i < 32 or i > 126:
+            c = 'O'  # octal
+        else:
+            c = 'P'  # printable
+        f.write(c)
+    f.write("\";\n\n")
+
+    f.write("extern const char cUnescapeTable[] =\n")
+    for i in range(0, 256):
+        if i % 64 == 0:
+            if i != 0:
+                f.write("\"\n")
+            f.write("  \"")
+        c = chr(i)
+        if c in '\'?':
+            f.write(c)
+        elif c in '"\\abfnrtv':
+            f.write("\\" + c)
+        elif i >= ord('0') and i <= ord('7'):
+            f.write("O")  # octal
+        elif c == "x":
+            f.write("X")  # hex
+        else:
+            f.write("I")  # invalid
+    f.write("\";\n\n")
+
+    f.write("extern const unsigned char hexTable[] = {")
+    for i in range(0, 256):
+        if i % 16 == 0:
+            f.write("\n  ")
+        if i >= ord('0') and i <= ord('9'):
+            f.write("{0:2d}, ".format(i - ord('0')))
+        elif i >= ord('a') and i <= ord('f'):
+            f.write("{0:2d}, ".format(i - ord('a') + 10))
+        elif i >= ord('A') and i <= ord('F'):
+            f.write("{0:2d}, ".format(i - ord('A') + 10))
+        else:
+            f.write("16, ")
+    f.write("\n};\n\n")
+
+    # 0 = passthrough
+    # 1 = unused
+    # 2 = safe in path (/)
+    # 3 = space (replace with '+' in query)
+    # 4 = always percent-encode
+    f.write("extern const unsigned char uriEscapeTable[] = {")
+    passthrough = (
+        list(map(ord, '0123456789')) +
+        list(map(ord, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')) +
+        list(map(ord, 'abcdefghijklmnopqrstuvwxyz')) +
+        list(map(ord, '-_.~')))
+    for i in range(0, 256):
+        if i % 16 == 0:
+            f.write("\n  ")
+        if i in passthrough:
+            f.write("0, ")
+        elif i == ord('/'):
+            f.write("2, ")
+        elif i == ord(' '):
+            f.write("3, ")
+        else:
+            f.write("4, ")
+    f.write("\n};\n\n")
+
+    f.write("}  // namespace detail\n"
+            "}  // namespace folly\n")
+
+def main():
+    parser = OptionParser()
+    parser.add_option("--install_dir", dest="install_dir", default=".",
+                      help="write output to DIR", metavar="DIR")
+    parser.add_option("--fbcode_dir")
+    (options, args) = parser.parse_args()
+    f = open(os.path.join(options.install_dir, OUTPUT_FILE), "w")
+    generate(f)
+    f.close()
+
+if __name__ == "__main__":
+    main()
diff --git a/faux-folly/folly/build/generate_format_tables.py b/faux-folly/folly/build/generate_format_tables.py
new file mode 100755
index 0000000..45abc4c
--- /dev/null
+++ b/faux-folly/folly/build/generate_format_tables.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#
+# Generate Format tables
+
+
+import os
+from optparse import OptionParser
+
+OUTPUT_FILE = "FormatTables.cpp"
+
+def generate_table(f, type_name, name, map):
+    f.write("extern const {0} {1}[] = {{".format(type_name, name))
+    for i in range(0, 256):
+        if i % 2 == 0:
+            f.write("\n  ")
+        f.write("{0}::{1}, ".format(type_name, map.get(chr(i), "INVALID")))
+    f.write("\n};\n\n")
+
+def generate_conv_table(f, name, values):
+    values = list(values)
+    line = ''
+    for i, v in enumerate(values):
+        if i == 0:
+            f.write("extern const char {0}[{1}][{2}] = {{\n".format(
+                name, len(values), len(v)))
+        row = "{{{0}}}, ".format(", ".join("'{0}'".format(x) for x in v))
+        if len(line) + len(row) > 79:
+            f.write(line + "\n")
+            line = ''
+        line += row
+    if line:
+        f.write(line + "\n")
+    f.write("};\n\n")
+
+def octal_values():
+    return (tuple("{0:03o}".format(x)) for x in range(512))
+
+def hex_values(upper):
+    fmt = "{0:02X}" if upper else "{0:02x}"
+    return (tuple(fmt.format(x)) for x in range(256))
+
+def binary_values():
+    return (tuple("{0:08b}".format(x)) for x in range(256))
+
+def generate(f):
+    f.write("#include <folly/FormatArg.h>\n"
+            "\n"
+            "namespace folly {\n"
+            "namespace detail {\n"
+            "\n")
+
+    generate_table(
+        f, "FormatArg::Align", "formatAlignTable",
+        {"<": "LEFT", ">": "RIGHT", "=": "PAD_AFTER_SIGN", "^": "CENTER"})
+
+    generate_table(
+        f, "FormatArg::Sign", "formatSignTable",
+        {"+": "PLUS_OR_MINUS", "-": "MINUS", " ": "SPACE_OR_MINUS"})
+
+    generate_conv_table(f, "formatOctal", octal_values())
+    generate_conv_table(f, "formatHexLower", hex_values(False))
+    generate_conv_table(f, "formatHexUpper", hex_values(True))
+    generate_conv_table(f, "formatBinary", binary_values())
+
+    f.write("}  // namespace detail\n"
+            "}  // namespace folly\n")
+
+def main():
+    parser = OptionParser()
+    parser.add_option("--install_dir", dest="install_dir", default=".",
+                      help="write output to DIR", metavar="DIR")
+    parser.add_option("--fbcode_dir")
+    (options, args) = parser.parse_args()
+    f = open(os.path.join(options.install_dir, OUTPUT_FILE), "w")
+    generate(f)
+    f.close()
+
+if __name__ == "__main__":
+    main()
diff --git a/faux-folly/folly/configure.ac b/faux-folly/folly/configure.ac
new file mode 100644
index 0000000..dba3ce1
--- /dev/null
+++ b/faux-folly/folly/configure.ac
@@ -0,0 +1,402 @@
+
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+
+m4_define([folly_version_str], m4_esyscmd_s([cat VERSION]))
+
+AC_INIT([folly], m4_translit(folly_version_str, [:], [.]), [folly@fb.com])
+
+# We assume all revisions are backwards incompatible.
+LT_VERSION=folly_version_str:0
+AC_SUBST([LT_VERSION])
+
+AC_CONFIG_SRCDIR([Likely.h])
+AC_CONFIG_HEADERS([config.h])
+AX_PREFIX_CONFIG_H([folly-config.h], [folly], [config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+
+AM_INIT_AUTOMAKE([foreign dist-bzip2 nostdinc subdir-objects])
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AX_CONFIG_FEATURE_DEFAULT_DISABLED
+AX_CONFIG_FEATURE(
+        [deprecated-assoc],
+        [supports deprecated associative containers (hash_map/hash_set)],
+        [HAVE_DEPRECATED_ASSOC],
+        [Define if you want to support deprecated associative containers])
+
+AC_PROG_INSTALL
+AM_PROG_LIBTOOL
+
+AC_LANG([C++])
+
+# Checks for programs.
+AC_PROG_CXX
+AC_PROG_CC
+AC_CXX_COMPILE_STDCXX_0X
+
+# Be sure to add any -std option to CXXFLAGS before we invoke any
+# AC_COMPILE_IFELSE() or similar macros. Any such macros that are invoked
+# before we update CXXFLAGS will not be run with the same options that we use
+# during the real build.
+STD=""
+if test "x$ac_cv_cxx_compile_cxx0x_cxx" = xyes; then
+   STD="-std=c++0x"
+fi
+if test "x$ac_cv_cxx_compile_cxx0x_gxx" = xyes; then
+   STD="-std=gnu++0x"
+fi
+
+CXXFLAGS="$STD $CXXFLAGS"
+
+# Checks for glog and gflags
+# There are no symbols with C linkage, so we do a try-run
+AC_HAVE_LIBRARY([glog],[],[AC_MSG_ERROR(
+                [Please install google-glog library])])
+AC_CACHE_CHECK(
+  [for glog viability],
+  [folly_cv_prog_cc_glog],
+  [AC_RUN_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <glog/logging.h>
+      int main(int argc, char** argv) {
+        google::InitGoogleLogging(argv[0]);
+        google::ShutdownGoogleLogging();
+        return 0;
+      }
+    ]],
+    [folly_cv_prog_cc_glog=yes],
+    [folly_cv_prog_cc_glog=no]
+  )]
+)
+
+if test "$folly_cv_prog_cc_glog" != "yes"; then
+  AC_MSG_ERROR(["libglog invalid, see config.log for details"])
+fi
+
+AC_HAVE_LIBRARY([gflags],[],[AC_MSG_ERROR(
+                [Please install google-gflags library])])
+AC_CACHE_CHECK(
+  [for gflags viability],
+  [folly_cv_prog_cc_gflags],
+  [AC_RUN_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <gflags/gflags.h>
+      DEFINE_bool(folly_truthy, true, "Sample truthy flag");
+      DEFINE_bool(folly_falsey, false, "Sample falsey flag");
+      int main(int argc, char** argv) {
+        return (FLAGS_folly_truthy && !FLAGS_folly_falsey) ? 0 : 1;
+      }
+    ]],
+    [folly_cv_prog_cc_gflags=yes],
+    [folly_cv_prog_cc_gflags=no]
+  )]
+)
+
+if test "$folly_cv_prog_cc_gflags" != "yes"; then
+  AC_MSG_ERROR(["libgflags invalid, see config.log for details"])
+fi
+
+# check for boost libs
+AX_BOOST_BASE([1.51.0], [], [AC_MSG_ERROR(
+              [Please install boost >= 1.51.0 (thread, regex, and system)])])
+AX_BOOST_THREAD
+AX_BOOST_REGEX
+AX_BOOST_SYSTEM
+
+# Check for python interpreter
+AM_PATH_PYTHON
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([fcntl.h features.h inttypes.h limits.h stdint.h stdlib.h string.h sys/time.h unistd.h mutex.h malloc.h emmintrin.h byteswap.h bits/functexcept.h bits/c++config.h])
+
+AC_CHECK_HEADER(double-conversion/double-conversion.h, [], [AC_MSG_ERROR(
+                [Couldn't find double-conversion.h, please download from \
+                 https://github.com/google/double-conversion/])], [])
+AC_CHECK_LIB([double-conversion],[ceil],[],[AC_MSG_ERROR(
+             [Please install double-conversion library])])
+
+AC_CHECK_LIB([event], [event_set], [], [AC_MSG_ERROR([Unable to find libevent])])
+
+AC_CHECK_LIB([jemalloc], [xallocx])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_C_VOLATILE
+AC_CHECK_TYPE([__int128], AC_DEFINE([HAVE_INT128_T], [1], [Define if we have __int128]))
+AC_CHECK_TYPES([ptrdiff_t, pthread_spinlock_t])
+
+AC_CACHE_CHECK(
+  [for ifunc support],
+  [folly_cv_prog_cc_ifunc],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #pragma GCC diagnostic error "-Wattributes"
+      extern "C" void (*test_ifunc(void))() { return 0; }
+      void func() __attribute__((ifunc("test_ifunc")));]
+    ],
+    [folly_cv_prog_cc_ifunc=yes],
+    [folly_cv_prog_cc_ifunc=no])])
+
+if test "$folly_cv_prog_cc_ifunc" = "yes"; then
+  AC_DEFINE([HAVE_IFUNC], [1], [Define to 1 if the compiler supports ifunc])
+fi
+
+AC_CACHE_CHECK(
+  [for final and override support],
+  [folly_cv_c_final_override],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[class C { virtual void f() final {} virtual void g() {} };
+                    class D : public C { virtual void g() override {} };]],
+    [folly_cv_c_final_override=yes],
+    [folly_cv_c_final_override=no])])
+
+if test "$folly_cv_c_final_override" = "yes"; then
+  final_val=final
+  override_val=override
+else
+  final_val=
+  override_val=
+fi
+
+AC_DEFINE_UNQUOTED(
+  [FINAL], [$final_val],
+  [Define to "final" if the compiler supports C++11 "final"])
+AC_DEFINE_UNQUOTED(
+  [OVERRIDE], [$override_val],
+  [Define to "override" if the compiler supports C++11 "override"])
+
+AC_CACHE_CHECK(
+  [for std::this_thread::sleep_for],
+  [folly_cv_func_this_thread_sleep_for],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <thread>
+      #include <chrono>
+      void func() { std::this_thread::sleep_for(std::chrono::seconds(1)); }]],
+    [folly_cv_func_this_thread_sleep_for=yes],
+    [folly_cv_func_this_thread_sleep_for=no])])
+
+if test "$folly_cv_func_this_thread_sleep_for" = yes; then
+    AC_DEFINE([HAVE_STD__THIS_THREAD__SLEEP_FOR], [1],
+              [Define to 1 if std::this_thread::sleep_for() is defined.])
+fi
+
+AC_CACHE_CHECK(
+  [for constexpr strlen],
+  [folly_cv_func_constexpr_strlen],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <cstring>
+      static constexpr int val = strlen("foo");]],
+    [folly_cv_func_constexpr_strlen=yes],
+    [folly_cv_func_constexpr_strlen=no])])
+
+if test "$folly_cv_func_constexpr_strlen" = yes; then
+    AC_DEFINE([HAVE_CONSTEXPR_STRLEN], [1],
+              [Define to 1 if strlen(3) is constexpr.])
+fi
+
+AC_CACHE_CHECK(
+  [for libc++],
+  [folly_cv_lib_libcpp],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <type_traits>
+      #if !_LIBCPP_VERSION
+      #error No libc++
+      #endif
+      void func() {}]
+    ],
+    [folly_cv_lib_libcpp=yes],
+    [folly_cv_lib_libcpp=no])])
+
+if test "$folly_cv_lib_libcpp" = yes; then
+  AC_DEFINE([USE_LIBCPP], [1], [Define to 1 if we're using libc++.])
+fi
+
+AC_CACHE_CHECK(
+  [for usable std::is_trivially_copyable],
+  [folly_cv_decl_std_is_trivially_copyable],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <type_traits>
+      const bool val = std::is_trivially_copyable<bool>::value;]
+    ],
+    [folly_cv_decl_std_is_trivially_copyable=yes],
+    [folly_cv_decl_std_is_trivially_copyable=no])])
+
+if test "$folly_cv_decl_std_is_trivially_copyable" = yes; then
+  AC_DEFINE([HAVE_STD__IS_TRIVIALLY_COPYABLE], [1],
+            [Define to 1 if we have a usable std::is_trivially_copyable<T>
+             implementation.])
+fi
+
+AC_CACHE_CHECK(
+  [gflags namespace],
+  [folly_cv_decl_gflags_namespace],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <gflags/gflags.h>
+      void foo() { gflags::GetArgv(); }]
+    ],
+    [folly_cv_decl_gflags_namespace=gflags],
+    [AC_COMPILE_IFELSE(
+      [AC_LANG_SOURCE[
+        #include <gflags/gflags.h>
+        void foo() { google::GetArgv(); }]
+      ],
+      [folly_cv_decl_gflags_namespace=google],
+      [folly_cv_decl_gflags_namespace=error])])])
+
+if test "$folly_cv_decl_gflags_namespace" = error; then
+  AC_MSG_ERROR([Cannot determine gflags namespace])
+else
+  AC_DEFINE_UNQUOTED(
+    [GFLAGS_NAMESPACE], [$folly_cv_decl_gflags_namespace],
+    [Define to gflags namespace (usually "google" or "gflags")])
+  if test "$folly_cv_decl_gflags_namespace" != gflags; then
+     AC_DEFINE([UNUSUAL_GFLAGS_NAMESPACE], [1],
+               [Define to 1 if the gflags namespace is not "gflags"])
+  fi
+fi
+
+# Figure out if we support weak symbols. If not, we will link in some null
+# stubs for functions that would otherwise be weak.
+AC_CACHE_CHECK(
+  [for weak symbol support],
+  [folly_cv_prog_cc_weak_symbols],
+  [AC_LINK_IFELSE(
+    [AC_LANG_SOURCE[
+      extern "C" void configure_link_extern_weak_test() __attribute__((weak));
+      int main(int argc, char** argv) {
+          return configure_link_extern_weak_test == nullptr;
+      }]],
+    [folly_cv_prog_cc_weak_symbols="yes"],
+    [folly_cv_prog_cc_weak_symbols="no"])])
+
+if test "$folly_cv_prog_cc_weak_symbols" = yes; then
+  AC_DEFINE([HAVE_WEAK_SYMBOLS], [1],
+            [Define to 1 if the linker supports weak symbols.])
+fi
+
+AC_CACHE_CHECK(
+  [for vsnprintf reporting bad format strings],
+  [folly_cv_prog_vsnprintf_bad_format],
+  [AC_RUN_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <stdio.h>
+
+      int main(int argc, char** argv) {
+          char buf[256];
+          return vsnprintf(buf, sizeof(buf), "%", 1) < 0 ? 0 : 1;
+      }]],
+    [folly_cv_prog_vsnprintf_bad_format="yes"],
+    [folly_cv_prog_vsnprintf_bad_format="no"])])
+
+if test "$folly_cv_prog_vsnprintf_bad_format" = yes; then
+  AC_DEFINE([HAVE_VSNPRINTF_ERRORS], [1],
+            [Define to 1 if the vsnprintf supports returning errors on bad format strings.])
+fi
+
+AC_SEARCH_LIBS([cplus_demangle_v3_callback], [iberty_pic iberty])
+if test "$ac_cv_search_cplus_demangle_v3_callback" != "no" ; then
+  AC_DEFINE([HAVE_CPLUS_DEMANGLE_V3_CALLBACK], [1],
+            [Define to 1 if we have cplus_demangle_v3_callback.])
+fi
+
+# Check for clock_gettime(2). This is not in an AC_CHECK_FUNCS() because we
+# want to link with librt if necessary.
+AC_SEARCH_LIBS([clock_gettime], [rt],
+  AC_DEFINE(
+    [HAVE_CLOCK_GETTIME],
+    [1],
+    [Define to 1 if we support clock_gettime(2).]),
+  [])
+
+# Check for pthread_atfork(3). This is not in an AC_CHECK_FUNCS() because we
+# want to include pthread.h if necessary.
+AC_CACHE_CHECK(
+  [for pthread_atfork support],
+  [folly_cv_prog_cc_pthread_atfork],
+  [AC_COMPILE_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <pthread.h>
+      void func() {pthread_atfork(NULL, NULL, NULL);}]
+    ],
+    [folly_cv_prog_cc_pthread_atfork=yes],
+    [folly_cv_prog_cc_pthread_atfork=no])])
+
+if test "$folly_cv_prog_cc_pthread_atfork" = "yes"; then
+  AC_DEFINE([HAVE_PTHREAD_ATFORK], [1], [Define to 1 if the compiler supports pthread_atfork])
+fi
+
+# Check for XSI-compatible strerror_r as default implementation
+AC_CACHE_CHECK(
+  [for XSI style strerror_r support],
+  [folly_cv_prog_cc_xsi_strerror_r],
+  [AC_RUN_IFELSE(
+    [AC_LANG_SOURCE[
+      #include <string.h>
+      #include <errno.h>
+      int main(int argc, char** argv) {
+        char buf[1024];
+        buf[0] = 0;
+        int ret = strerror_r(ENOMEM, buf, sizeof(buf));
+        return ret;
+      }
+    ]],
+    [folly_cv_prog_cc_xsi_strerror_r=yes],
+    [folly_cv_prog_cc_xsi_strerror_r=no])])
+
+if test "$folly_cv_prog_cc_xsi_strerror_r" = "yes"; then
+  AC_DEFINE([HAVE_XSI_STRERROR_R], [1], [Define to 1 if the runtime supports XSI-style strerror_r])
+fi
+
+# Checks for library functions.
+AC_CHECK_FUNCS([getdelim \
+                gettimeofday \
+                memmove \
+                memset \
+                pow \
+                strerror \
+                pthread_yield \
+                malloc_size \
+                malloc_usable_size \
+                memrchr \
+                pipe2])
+
+if test "$ac_cv_func_pthread_yield" = "no"; then
+   AC_CHECK_HEADERS([sched.h])
+   AC_CHECK_FUNCS([sched_yield])
+fi
+
+# Include directory that contains "folly" so #include <folly/Foo.h> works
+AM_CPPFLAGS='-I$(top_srcdir)/..'
+AM_CPPFLAGS="$AM_CPPFLAGS $BOOST_CPPFLAGS"
+AM_LDFLAGS="$AM_LDFLAGS $BOOST_THREAD_LIB"
+AM_LDFLAGS="$AM_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_REGEX_LIB -lpthread"
+
+AC_SUBST([AM_CPPFLAGS])
+AC_SUBST([AM_LDFLAGS])
+
+AM_CONDITIONAL([HAVE_STD_THREAD], [test "$ac_cv_header_features" = "yes"])
+AM_CONDITIONAL([HAVE_X86_64], [test "$build_cpu" = "x86_64"])
+AM_CONDITIONAL([HAVE_LINUX], [test "$build_os" == "linux-gnu"])
+AM_CONDITIONAL([HAVE_WEAK_SYMBOLS],
+               [test "$folly_cv_prog_cc_weak_symbols" = "yes"])
+AM_CONDITIONAL([HAVE_BITS_FUNCTEXCEPT], [test "$ac_cv_header_bits_functexcept_h" = "yes"])
+
+# Output
+AC_CONFIG_FILES([Makefile
+                 test/Makefile
+                 test/function_benchmark/Makefile])
+AC_OUTPUT
diff --git a/faux-folly/folly/detail/BitIteratorDetail.h b/faux-folly/folly/detail/BitIteratorDetail.h
new file mode 100644
index 0000000..a328106
--- /dev/null
+++ b/faux-folly/folly/detail/BitIteratorDetail.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_BITITERATORDETAIL_H_
+#define FOLLY_DETAIL_BITITERATORDETAIL_H_
+
+#include <iterator>
+#include <type_traits>
+#include <boost/iterator/iterator_adaptor.hpp>
+
+namespace folly {
+
+template <class BaseIter> class BitIterator;
+
+namespace bititerator_detail {
+
+// Reference to a bit.
+// Templatize on both parent reference and value types to capture
+// const-ness correctly and to work with the case where Ref is a
+// reference-like type (not T&), just like our BitReference here.
+template <class Ref, class Value>
+class BitReference {
+ public:
+  BitReference(Ref r, size_t bit) : ref_(r), bit_(bit) { }
+
+  /* implicit */ operator bool() const {
+    return ref_ & (one_ << bit_);
+  }
+
+  BitReference& operator=(bool b) {
+    if (b) {
+      set();
+    } else {
+      clear();
+    }
+    return *this;
+  }
+
+  void set() {
+    ref_ |= (one_ << bit_);
+  }
+
+  void clear() {
+    ref_ &= ~(one_ << bit_);
+  }
+
+  void flip() {
+    ref_ ^= (one_ << bit_);
+  }
+
+ private:
+  // shortcut to avoid writing static_cast everywhere
+  const static Value one_ = 1;
+
+  Ref ref_;
+  size_t bit_;
+};
+
+template <class BaseIter>
+struct BitIteratorBase {
+  static_assert(std::is_integral<typename BaseIter::value_type>::value,
+                "BitIterator may only be used with integral types");
+  typedef boost::iterator_adaptor<
+    BitIterator<BaseIter>,      // Derived
+    BaseIter,                   // Base
+    bool,                       // Value
+    boost::use_default,         // CategoryOrTraversal
+    bititerator_detail::BitReference<
+      typename std::iterator_traits<BaseIter>::reference,
+      typename std::iterator_traits<BaseIter>::value_type
+    >,  // Reference
+    ssize_t> type;
+};
+
+
+}  // namespace bititerator_detail
+}  // namespace folly
+
+#endif /* FOLLY_DETAIL_BITITERATORDETAIL_H_ */
diff --git a/faux-folly/folly/detail/BitsDetail.h b/faux-folly/folly/detail/BitsDetail.h
new file mode 100644
index 0000000..d526338
--- /dev/null
+++ b/faux-folly/folly/detail/BitsDetail.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_BITSDETAIL_H_
+#define FOLLY_DETAIL_BITSDETAIL_H_
+
+namespace folly {
+namespace detail {
+
+// If we're targeting an architecture with popcnt support, use
+// __builtin_popcount directly, as it's presumably inlined.
+// If not, use runtime detection using __attribute__((__ifunc__))
+// (see Bits.cpp)
+#ifdef _MSC_VER
+inline int popcount(unsigned int x) {
+  return __popcnt(x);
+}
+inline int popcountll(unsigned long long x) {
+  return __popcnt64(x);
+}
+#elif defined(__POPCNT__)
+
+inline int popcount(unsigned int x) {
+  return __builtin_popcount(x);
+}
+inline int popcountll(unsigned long long x) {
+  return __builtin_popcountll(x);
+}
+
+#else   /* !__POPCNT__ */
+
+int popcount(unsigned int x);
+int popcountll(unsigned long long x);
+
+#endif  /* !__POPCNT__ */
+
+}  // namespace detail
+}  // namespace folly
+
+#endif /* FOLLY_DETAIL_BITSDETAIL_H_ */
diff --git a/faux-folly/folly/detail/CacheLocality.cpp b/faux-folly/folly/detail/CacheLocality.cpp
new file mode 100644
index 0000000..41e3c13
--- /dev/null
+++ b/faux-folly/folly/detail/CacheLocality.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2015 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/detail/CacheLocality.h>
+
+#ifndef _MSC_VER
+#define _GNU_SOURCE 1 // for RTLD_NOLOAD
+#include <dlfcn.h>
+#endif
+#include <fstream>
+
+#include <folly/Conv.h>
+#include <folly/Exception.h>
+#include <folly/FileUtil.h>
+#include <folly/Format.h>
+#include <folly/ScopeGuard.h>
+
+namespace folly { namespace detail {
+
+///////////// CacheLocality
+
+/// Returns the best real CacheLocality information available
+static CacheLocality getSystemLocalityInfo() {
+#ifdef __linux__
+  try {
+    return CacheLocality::readFromSysfs();
+  } catch (...) {
+    // keep trying
+  }
+#endif
+
+  long numCpus = sysconf(_SC_NPROCESSORS_CONF);
+  if (numCpus <= 0) {
+    // This shouldn't happen, but if it does we should try to keep
+    // going.  We are probably not going to be able to parse /sys on
+    // this box either (although we will try), which means we are going
+    // to fall back to the SequentialThreadId splitter.  On my 16 core
+    // (x hyperthreading) dev box 16 stripes is enough to get pretty good
+    // contention avoidance with SequentialThreadId, and there is little
+    // improvement from going from 32 to 64.  This default gives us some
+    // wiggle room
+    numCpus = 32;
+  }
+  return CacheLocality::uniform(numCpus);
+}
+
+template <>
+const CacheLocality& CacheLocality::system<std::atomic>() {
+  static CacheLocality cache(getSystemLocalityInfo());
+  return cache;
+}
+
+// Each level of cache has sharing sets, which are the set of cpus
+// that share a common cache at that level.  These are available in a
+// hex bitset form (/sys/devices/system/cpu/cpu0/index0/shared_cpu_map,
+// for example).  They are also available in a human-readable list form,
+// as in /sys/devices/system/cpu/cpu0/index0/shared_cpu_list.  The list
+// is a comma-separated list of numbers and ranges, where the ranges are
+// a pair of decimal numbers separated by a '-'.
+//
+// To sort the cpus for optimum locality we don't really need to parse
+// the sharing sets, we just need a unique representative from the
+// equivalence class.  The smallest value works fine, and happens to be
+// the first decimal number in the file.  We load all of the equivalence
+// class information from all of the cpu*/index* directories, order the
+// cpus first by increasing last-level cache equivalence class, then by
+// the smaller caches.  Finally, we break ties with the cpu number itself.
+
+/// Returns the first decimal number in the string, or throws an exception
+/// if the string does not start with a number terminated by ',', '-',
+/// '\n', or eos.
+static size_t parseLeadingNumber(const std::string& line) {
+  auto raw = line.c_str();
+  char *end;
+  unsigned long val = strtoul(raw, &end, 10);
+  if (end == raw || (*end != ',' && *end != '-' && *end != '\n' && *end != 0)) {
+    throw std::runtime_error(to<std::string>(
+        "error parsing list '", line, "'").c_str());
+  }
+  return val;
+}
+
+CacheLocality CacheLocality::readFromSysfsTree(
+    const std::function<std::string(std::string)>& mapping) {
+  // number of equivalence classes per level
+  std::vector<size_t> numCachesByLevel;
+
+  // the list of cache equivalence classes, where equivalance classes
+  // are named by the smallest cpu in the class
+  std::vector<std::vector<size_t>> equivClassesByCpu;
+
+  std::vector<size_t> cpus;
+
+  while (true) {
+    auto cpu = cpus.size();
+    std::vector<size_t> levels;
+    for (size_t index = 0; ; ++index) {
+      auto dir = format("/sys/devices/system/cpu/cpu{}/cache/index{}/",
+                        cpu, index).str();
+      auto cacheType = mapping(dir + "type");
+      auto equivStr = mapping(dir + "shared_cpu_list");
+      if (cacheType.size() == 0 || equivStr.size() == 0) {
+        // no more caches
+        break;
+      }
+      if (cacheType[0] == 'I') {
+        // cacheType in { "Data", "Instruction", "Unified" }. skip icache
+        continue;
+      }
+      auto equiv = parseLeadingNumber(equivStr);
+      auto level = levels.size();
+      levels.push_back(equiv);
+
+      if (equiv == cpu) {
+        // we only want to count the equiv classes once, so we do it when
+        // we first encounter them
+        while (numCachesByLevel.size() <= level) {
+          numCachesByLevel.push_back(0);
+        }
+        numCachesByLevel[level]++;
+      }
+    }
+
+    if (levels.size() == 0) {
+      // no levels at all for this cpu, we must be done
+      break;
+    }
+    equivClassesByCpu.emplace_back(std::move(levels));
+    cpus.push_back(cpu);
+  }
+
+  if (cpus.size() == 0) {
+    throw std::runtime_error("unable to load cache sharing info");
+  }
+
+  std::sort(cpus.begin(), cpus.end(), [&](size_t lhs, size_t rhs) -> bool {
+    // sort first by equiv class of cache with highest index, direction
+    // doesn't matter.  If different cpus have different numbers of
+    // caches then this code might produce a sub-optimal ordering, but
+    // it won't crash
+    auto& lhsEquiv = equivClassesByCpu[lhs];
+    auto& rhsEquiv = equivClassesByCpu[rhs];
+    for (int i = std::min(lhsEquiv.size(), rhsEquiv.size()) - 1; i >= 0; --i) {
+      if (lhsEquiv[i] != rhsEquiv[i]) {
+        return lhsEquiv[i] < rhsEquiv[i];
+      }
+    }
+
+    // break ties deterministically by cpu
+    return lhs < rhs;
+  });
+
+  // the cpus are now sorted by locality, with neighboring entries closer
+  // to each other than entries that are far away.  For striping we want
+  // the inverse map, since we are starting with the cpu
+  std::vector<size_t> indexes(cpus.size());
+  for (size_t i = 0; i < cpus.size(); ++i) {
+    indexes[cpus[i]] = i;
+  }
+
+  return CacheLocality{
+      cpus.size(), std::move(numCachesByLevel), std::move(indexes) };
+}
+
+CacheLocality CacheLocality::readFromSysfs() {
+  return readFromSysfsTree([](std::string name) {
+    std::ifstream xi(name.c_str());
+    std::string rv;
+    std::getline(xi, rv);
+    return rv;
+  });
+}
+
+
+CacheLocality CacheLocality::uniform(size_t numCpus) {
+  CacheLocality rv;
+
+  rv.numCpus = numCpus;
+
+  // one cache shared by all cpus
+  rv.numCachesByLevel.push_back(numCpus);
+
+  // no permutations in locality index mapping
+  for (size_t cpu = 0; cpu < numCpus; ++cpu) {
+    rv.localityIndexByCpu.push_back(cpu);
+  }
+
+  return rv;
+}
+
+////////////// Getcpu
+
+/// Resolves the dynamically loaded symbol __vdso_getcpu, returning null
+/// on failure
+static Getcpu::Func loadVdsoGetcpu() {
+/* The bionic dynamic linker doesn't take too kindly to this bit of code.
+ * And unloading the library is extremely bad form.
+ */
+#ifdef _MSC_VER
+  return nullptr;
+#elif !defined(ANDROID) && !defined(FOLLY_NO_LIBDL)
+  void* h = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+  if (h == nullptr) {
+    return nullptr;
+  }
+
+  auto func = Getcpu::Func(dlsym(h, "__vdso_getcpu"));
+  if (func == nullptr) {
+    // technically a null result could either be a failure or a successful
+    // lookup of a symbol with the null value, but the second can't actually
+    // happen for this symbol.  No point holding the handle forever if
+    // we don't need the code
+    dlclose(h);
+  }
+
+  return func;
+#else
+  return nullptr;
+#endif
+}
+
+Getcpu::Func Getcpu::vdsoFunc() {
+  static Func func = loadVdsoGetcpu();
+  return func;
+}
+
+/////////////// SequentialThreadId
+
+template<>
+std::atomic<size_t> SequentialThreadId<std::atomic>::prevId(0);
+
+template<>
+FOLLY_TLS size_t SequentialThreadId<std::atomic>::currentId(0);
+
+/////////////// AccessSpreader
+
+template<>
+const AccessSpreader<std::atomic>
+AccessSpreader<std::atomic>::stripeByCore(
+    CacheLocality::system<>().numCachesByLevel.front());
+
+template<>
+const AccessSpreader<std::atomic>
+AccessSpreader<std::atomic>::stripeByChip(
+    CacheLocality::system<>().numCachesByLevel.back());
+
+template<>
+AccessSpreaderArray<std::atomic,128>
+AccessSpreaderArray<std::atomic,128>::sharedInstance = {};
+
+/// Always claims to be on CPU zero, node zero
+static int degenerateGetcpu(unsigned* cpu, unsigned* node, void* unused) {
+  if (cpu != nullptr) {
+    *cpu = 0;
+  }
+  if (node != nullptr) {
+    *node = 0;
+  }
+  return 0;
+}
+
+template<>
+Getcpu::Func AccessSpreader<std::atomic>::pickGetcpuFunc(size_t numStripes) {
+  if (numStripes == 1) {
+    // there's no need to call getcpu if there is only one stripe.
+    // This should not be common, so we don't want to waste a test and
+    // branch in the main code path, but we might as well use a faster
+    // function pointer
+    return &degenerateGetcpu;
+  } else {
+    auto best = Getcpu::vdsoFunc();
+    return best ? best : &SequentialThreadId<std::atomic>::getcpu;
+  }
+}
+
+} } // namespace folly::detail
diff --git a/faux-folly/folly/detail/CacheLocality.h b/faux-folly/folly/detail/CacheLocality.h
new file mode 100644
index 0000000..40d554f
--- /dev/null
+++ b/faux-folly/folly/detail/CacheLocality.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_CACHELOCALITY_H_
+#define FOLLY_DETAIL_CACHELOCALITY_H_
+
+#include <sched.h>
+#include <atomic>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <folly/Likely.h>
+#include <folly/Portability.h>
+
+namespace folly { namespace detail {
+
+// This file contains several classes that might be useful if you are
+// trying to dynamically optimize cache locality: CacheLocality reads
+// cache sharing information from sysfs to determine how CPUs should be
+// grouped to minimize contention, Getcpu provides fast access to the
+// current CPU via __vdso_getcpu, and AccessSpreader uses these two to
+// optimally spread accesses among a predetermined number of stripes.
+//
+// AccessSpreader<>::current(n) microbenchmarks at 22 nanos, which is
+// substantially less than the cost of a cache miss.  This means that we
+// can effectively use it to reduce cache line ping-pong on striped data
+// structures such as IndexedMemPool or statistics counters.
+//
+// Because CacheLocality looks at all of the cache levels, it can be
+// used for different levels of optimization.  AccessSpreader(2) does
+// per-chip spreading on a dual socket system.  AccessSpreader(numCpus)
+// does perfect per-cpu spreading.  AccessSpreader(numCpus / 2) does
+// perfect L1 spreading in a system with hyperthreading enabled.
+
+struct CacheLocality {
+
+  /// 1 more than the maximum value that can be returned from sched_getcpu
+  /// or getcpu.  This is the number of hardware thread contexts provided
+  /// by the processors
+  size_t numCpus;
+
+  /// Holds the number of caches present at each cache level (0 is
+  /// the closest to the cpu).  This is the number of AccessSpreader
+  /// stripes needed to avoid cross-cache communication at the specified
+  /// layer.  numCachesByLevel.front() is the number of L1 caches and
+  /// numCachesByLevel.back() is the number of last-level caches.
+  std::vector<size_t> numCachesByLevel;
+
+  /// A map from cpu (from sched_getcpu or getcpu) to an index in the
+  /// range 0..numCpus-1, where neighboring locality indices are more
+  /// likely to share caches then indices far away.  All of the members
+  /// of a particular cache level be contiguous in their locality index.
+  /// For example, if numCpus is 32 and numCachesByLevel.back() is 2,
+  /// then cpus with a locality index < 16 will share one last-level
+  /// cache and cpus with a locality index >= 16 will share the other.
+  std::vector<size_t> localityIndexByCpu;
+
+
+  /// Returns the best CacheLocality information available for the current
+  /// system, cached for fast access.  This will be loaded from sysfs if
+  /// possible, otherwise it will be correct in the number of CPUs but
+  /// not in their sharing structure.
+  ///
+  /// If you are into yo dawgs, this is a shared cache of the local
+  /// locality of the shared caches.
+  ///
+  /// The template parameter here is used to allow injection of a
+  /// repeatable CacheLocality structure during testing.  Rather than
+  /// inject the type of the CacheLocality provider into every data type
+  /// that transitively uses it, all components select between the default
+  /// sysfs implementation and a deterministic implementation by keying
+  /// off the type of the underlying atomic.  See DeterministicScheduler.
+  template <template<typename> class Atom = std::atomic>
+  static const CacheLocality& system();
+
+
+  /// Reads CacheLocality information from a tree structured like
+  /// the sysfs filesystem.  The provided function will be evaluated
+  /// for each sysfs file that needs to be queried.  The function
+  /// should return a string containing the first line of the file
+  /// (not including the newline), or an empty string if the file does
+  /// not exist.  The function will be called with paths of the form
+  /// /sys/devices/system/cpu/cpu*/cache/index*/{type,shared_cpu_list} .
+  /// Throws an exception if no caches can be parsed at all.
+  static CacheLocality readFromSysfsTree(
+      const std::function<std::string(std::string)>& mapping);
+
+  /// Reads CacheLocality information from the real sysfs filesystem.
+  /// Throws an exception if no cache information can be loaded.
+  static CacheLocality readFromSysfs();
+
+  /// Returns a usable (but probably not reflective of reality)
+  /// CacheLocality structure with the specified number of cpus and a
+  /// single cache level that associates one cpu per cache.
+  static CacheLocality uniform(size_t numCpus);
+
+  enum {
+    /// Memory locations on the same cache line are subject to false
+    /// sharing, which is very bad for performance.  Microbenchmarks
+    /// indicate that pairs of cache lines also see interference under
+    /// heavy use of atomic operations (observed for atomic increment on
+    /// Sandy Bridge).  See FOLLY_ALIGN_TO_AVOID_FALSE_SHARING
+    kFalseSharingRange = 128
+  };
+
+  static_assert(kFalseSharingRange == 128,
+      "FOLLY_ALIGN_TO_AVOID_FALSE_SHARING should track kFalseSharingRange");
+};
+
+// TODO replace __attribute__ with alignas and 128 with kFalseSharingRange
+
+/// An attribute that will cause a variable or field to be aligned so that
+/// it doesn't have false sharing with anything at a smaller memory address.
+#define FOLLY_ALIGN_TO_AVOID_FALSE_SHARING FOLLY_ALIGNED(128)
+
+/// Holds a function pointer to the VDSO implementation of getcpu(2),
+/// if available
+struct Getcpu {
+  /// Function pointer to a function with the same signature as getcpu(2).
+  typedef int (*Func)(unsigned* cpu, unsigned* node, void* unused);
+
+  /// Returns a pointer to the VDSO implementation of getcpu(2), if
+  /// available, or nullptr otherwise
+  static Func vdsoFunc();
+};
+
+/// A class that lazily binds a unique (for each implementation of Atom)
+/// identifier to a thread.  This is a fallback mechanism for the access
+/// spreader if we are in testing (using DeterministicAtomic) or if
+/// __vdso_getcpu can't be dynamically loaded
+template <template<typename> class Atom>
+struct SequentialThreadId {
+
+  /// Returns the thread id assigned to the current thread
+  static size_t get() {
+    auto rv = currentId;
+    if (UNLIKELY(rv == 0)) {
+      rv = currentId = ++prevId;
+    }
+    return rv;
+  }
+
+  /// Fills the thread id into the cpu and node out params (if they
+  /// are non-null).  This method is intended to act like getcpu when a
+  /// fast-enough form of getcpu isn't available or isn't desired
+  static int getcpu(unsigned* cpu, unsigned* node, void* unused) {
+    auto id = get();
+    if (cpu) {
+      *cpu = id;
+    }
+    if (node) {
+      *node = id;
+    }
+    return 0;
+  }
+
+ private:
+  static Atom<size_t> prevId;
+
+  static FOLLY_TLS size_t currentId;
+};
+
+template <template<typename> class Atom, size_t kMaxCpus>
+struct AccessSpreaderArray;
+
+/// AccessSpreader arranges access to a striped data structure in such a
+/// way that concurrently executing threads are likely to be accessing
+/// different stripes.  It does NOT guarantee uncontended access.
+/// Your underlying algorithm must be thread-safe without spreading, this
+/// is merely an optimization.  AccessSpreader::current(n) is typically
+/// much faster than a cache miss (22 nanos on my dev box, tested fast
+/// in both 2.6 and 3.2 kernels).
+///
+/// You are free to create your own AccessSpreader-s or to cache the
+/// results of AccessSpreader<>::shared(n), but you will probably want
+/// to use one of the system-wide shared ones.  Calling .current() on
+/// a particular AccessSpreader instance only saves about 1 nanosecond
+/// over calling AccessSpreader<>::shared(n).
+///
+/// If available (and not using the deterministic testing implementation)
+/// AccessSpreader uses the getcpu system call via VDSO and the
+/// precise locality information retrieved from sysfs by CacheLocality.
+/// This provides optimal anti-sharing at a fraction of the cost of a
+/// cache miss.
+///
+/// When there are not as many stripes as processors, we try to optimally
+/// place the cache sharing boundaries.  This means that if you have 2
+/// stripes and run on a dual-socket system, your 2 stripes will each get
+/// all of the cores from a single socket.  If you have 16 stripes on a
+/// 16 core system plus hyperthreading (32 cpus), each core will get its
+/// own stripe and there will be no cache sharing at all.
+///
+/// AccessSpreader has a fallback mechanism for when __vdso_getcpu can't be
+/// loaded, or for use during deterministic testing.  Using sched_getcpu or
+/// the getcpu syscall would negate the performance advantages of access
+/// spreading, so we use a thread-local value and a shared atomic counter
+/// to spread access out.
+///
+/// AccessSpreader is templated on the template type that is used
+/// to implement atomics, as a way to instantiate the underlying
+/// heuristics differently for production use and deterministic unit
+/// testing.  See DeterministicScheduler for more.  If you aren't using
+/// DeterministicScheduler, you can just use the default template parameter
+/// all of the time.
+template <template<typename> class Atom = std::atomic>
+struct AccessSpreader {
+
+  /// Returns a never-destructed shared AccessSpreader instance.
+  /// numStripes should be > 0.
+  static const AccessSpreader& shared(size_t numStripes) {
+    // sharedInstances[0] actually has numStripes == 1
+    assert(numStripes > 0);
+
+    // the last shared element handles all large sizes
+    return AccessSpreaderArray<Atom,kMaxCpus>::sharedInstance[
+        std::min(size_t(kMaxCpus), numStripes)];
+  }
+
+  /// Returns the stripe associated with the current CPU, assuming
+  /// that there are numStripes (non-zero) stripes.  Equivalent to
+  /// AccessSpreader::shared(numStripes)->current.
+  static size_t current(size_t numStripes) {
+    return shared(numStripes).current();
+  }
+
+  /// stripeByCore uses 1 stripe per L1 cache, according to
+  /// CacheLocality::system<>().  Use stripeByCore.numStripes() to see
+  /// its width, or stripeByCore.current() to get the current stripe
+  static const AccessSpreader stripeByCore;
+
+  /// stripeByChip uses 1 stripe per last-level cache, which is the fewest
+  /// number of stripes for which off-chip communication can be avoided
+  /// (assuming all caches are on-chip).  Use stripeByChip.numStripes()
+  /// to see its width, or stripeByChip.current() to get the current stripe
+  static const AccessSpreader stripeByChip;
+
+
+  /// Constructs an AccessSpreader that will return values from
+  /// 0 to numStripes-1 (inclusive), precomputing the mapping
+  /// from CPU to stripe.  There is no use in having more than
+  /// CacheLocality::system<Atom>().localityIndexByCpu.size() stripes or
+  /// kMaxCpus stripes
+  explicit AccessSpreader(size_t spreaderNumStripes,
+                          const CacheLocality& cacheLocality =
+                              CacheLocality::system<Atom>(),
+                          Getcpu::Func getcpuFunc = nullptr)
+    : getcpuFunc_(getcpuFunc ? getcpuFunc : pickGetcpuFunc(spreaderNumStripes))
+    , numStripes_(spreaderNumStripes)
+  {
+    auto n = cacheLocality.numCpus;
+    for (size_t cpu = 0; cpu < kMaxCpus && cpu < n; ++cpu) {
+      auto index = cacheLocality.localityIndexByCpu[cpu];
+      assert(index < n);
+      // as index goes from 0..n, post-transform value goes from
+      // 0..numStripes
+      stripeByCpu[cpu] = (index * numStripes_) / n;
+      assert(stripeByCpu[cpu] < numStripes_);
+    }
+    for (size_t cpu = n; cpu < kMaxCpus; ++cpu) {
+      stripeByCpu[cpu] = stripeByCpu[cpu - n];
+    }
+  }
+
+  /// Returns 1 more than the maximum value that can be returned from
+  /// current()
+  size_t numStripes() const {
+    return numStripes_;
+  }
+
+  /// Returns the stripe associated with the current CPU
+  size_t current() const {
+    unsigned cpu;
+    getcpuFunc_(&cpu, nullptr, nullptr);
+    return stripeByCpu[cpu % kMaxCpus];
+  }
+
+ private:
+
+  /// If there are more cpus than this nothing will crash, but there
+  /// might be unnecessary sharing
+  enum { kMaxCpus = 128 };
+
+  typedef uint8_t CompactStripe;
+
+  static_assert((kMaxCpus & (kMaxCpus - 1)) == 0,
+      "kMaxCpus should be a power of two so modulo is fast");
+  static_assert(kMaxCpus - 1 <= std::numeric_limits<CompactStripe>::max(),
+      "stripeByCpu element type isn't wide enough");
+
+
+  /// Points to the getcpu-like function we are using to obtain the
+  /// current cpu.  It should not be assumed that the returned cpu value
+  /// is in range.  We use a member for this instead of a static so that
+  /// this fetch preloads a prefix the stripeByCpu array
+  Getcpu::Func getcpuFunc_;
+
+  /// A precomputed map from cpu to stripe.  Rather than add a layer of
+  /// indirection requiring a dynamic bounds check and another cache miss,
+  /// we always precompute the whole array
+  CompactStripe stripeByCpu[kMaxCpus];
+
+  size_t numStripes_;
+
+  /// Returns the best getcpu implementation for this type and width
+  /// of AccessSpreader
+  static Getcpu::Func pickGetcpuFunc(size_t numStripes);
+};
+
+template<>
+Getcpu::Func AccessSpreader<std::atomic>::pickGetcpuFunc(size_t);
+
+
+/// An array of kMaxCpus+1 AccessSpreader<Atom> instances constructed
+/// with default params, with the zero-th element having 1 stripe
+template <template<typename> class Atom, size_t kMaxStripe>
+struct AccessSpreaderArray {
+
+  AccessSpreaderArray() {
+    for (size_t i = 0; i <= kMaxStripe; ++i) {
+      new (raw + i) AccessSpreader<Atom>(std::max(size_t(1), i));
+    }
+  }
+
+  ~AccessSpreaderArray() {
+    for (size_t i = 0; i <= kMaxStripe; ++i) {
+      auto p = static_cast<AccessSpreader<Atom>*>(static_cast<void*>(raw + i));
+      p->~AccessSpreader();
+    }
+  }
+
+  AccessSpreader<Atom> const& operator[] (size_t index) const {
+    return *static_cast<AccessSpreader<Atom> const*>(
+        static_cast<void const*>(raw + index));
+  }
+
+ private:
+
+  // AccessSpreader uses sharedInstance
+  friend AccessSpreader<Atom>;
+
+  static AccessSpreaderArray<Atom,kMaxStripe> sharedInstance;
+
+
+  /// aligned_storage is uninitialized, we use placement new since there
+  /// is no AccessSpreader default constructor
+  typename std::aligned_storage<sizeof(AccessSpreader<Atom>),
+                                CacheLocality::kFalseSharingRange>::type
+      raw[kMaxStripe + 1];
+};
+
+} }
+
+#endif /* FOLLY_DETAIL_CacheLocality_H_ */
diff --git a/faux-folly/folly/detail/CancellationDetail.h b/faux-folly/folly/detail/CancellationDetail.h
new file mode 100644
index 0000000..4507e98
--- /dev/null
+++ b/faux-folly/folly/detail/CancellationDetail.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015 Nest Labs, 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 <cassert>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+
+#ifdef __GNUC__
+#define FOLLY_CANCELLATION_WARN_UNUSED __attribute__((warn_unused_result))
+#else
+#define FOLLY_CANCELLATION_WARN_UNUSED
+#endif
+
+namespace folly {
+
+class Cancellation;
+
+namespace detail {
+
+/**
+ * The shared state object that implements the Cancellation object
+ *
+ * You must not use this class directly.
+ *
+ * This object is the <em>actual</em> Cancellation. It implements the
+ * std::BasicLockable interface, and therefore may be controlled by
+ * objects like std::lock_guard<T>.
+ */
+struct CancellationSharedState
+{
+public:
+    /* Cancellation shall *only* use the 'protected' or public methods.
+     */
+    friend class folly::Cancellation;
+
+    CancellationSharedState() {}
+    ~CancellationSharedState() {}
+
+    /* non-copyable */
+    CancellationSharedState(const CancellationSharedState& o) = delete;
+    CancellationSharedState& operator=(const CancellationSharedState& o) = delete;
+
+    /* non-moveable (thread safe move?) */
+    CancellationSharedState(CancellationSharedState&& o) = delete;
+    CancellationSharedState& operator=(CancellationSharedState&& o) = delete;
+
+    /* std::BasicLockable concept. Locks state. */
+    void lock()
+    {
+        bool have_lock = try_lock();
+        if (!have_lock) {
+            throw std::logic_error("Cancellation is cancelled, can't lock");
+        }
+    }
+
+    /* std::BasicLockable concept. Unocks state. */
+    void unlock()
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        assert(hold_state_refs_ > 0);
+        --hold_state_refs_;
+        if (hold_state_refs_ == 0) {
+            cond_.notify_all();
+        }
+    }
+
+    /* std::Lockable concept. Attempts to lock state. Returns true if NOT cancelled. */
+    bool try_lock()
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        if (is_cancelled_) {
+            return false;
+        }
+        ++hold_state_refs_;
+        assert(hold_state_refs_ != 0); /* overflow */
+        return true;
+    }
+
+protected:
+
+    bool is_cancelled() const
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        return is_cancelled_;
+    }
+
+    /* Set state to CANCELLED. Blocks if there are active state refs. */
+    void cancel()
+    {
+        // TODO: this implementation is subject to a live-lock
+        std::unique_lock<std::mutex> lock(mutex_);
+        ++cancel_refs_;
+        assert(cancel_refs_ != 0); /* overflow */
+        while (hold_state_refs_ > 0) {
+            cond_.wait(lock);
+        }
+        is_cancelled_ = true;
+        assert(cancel_refs_ > 0);
+        --cancel_refs_;
+    }
+
+private:
+    mutable std::mutex mutex_;
+    mutable std::condition_variable cond_;
+    unsigned cancel_refs_ {0};             //< number of cancel() calls currently active
+    unsigned hold_state_refs_ {0};         //< number of state refs currently active
+    bool is_cancelled_ {false};            //< state. false = not cancelled, true = cancelled.
+};
+
+} /* namespace detail */
+} /* namespace folly */
diff --git a/faux-folly/folly/detail/Clock.cpp b/faux-folly/folly/detail/Clock.cpp
new file mode 100644
index 0000000..d44a81c
--- /dev/null
+++ b/faux-folly/folly/detail/Clock.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 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/detail/Clock.h>
+
+#if __MACH__
+#include <errno.h>
+#include <mach/mach_time.h>
+
+static mach_timebase_info_data_t tb_info;
+static bool tb_init = mach_timebase_info(&tb_info) == KERN_SUCCESS;
+
+int clock_gettime(clockid_t clk_id, struct timespec* ts) {
+  if (!tb_init) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  uint64_t now_ticks = mach_absolute_time();
+  uint64_t now_ns = (now_ticks * tb_info.numer) / tb_info.denom;
+  ts->tv_sec = now_ns / 1000000000;
+  ts->tv_nsec = now_ns % 1000000000;
+
+  return 0;
+}
+
+int clock_getres(clockid_t clk_id, struct timespec* ts) {
+  if (!tb_init) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  ts->tv_sec = 0;
+  ts->tv_nsec = tb_info.numer / tb_info.denom;
+
+  return 0;
+}
+#elif _MSC_VER
+// using winpthreads from mingw-w64
+// <pthreads_time.h> has clock_gettime and friends
+// make sure to include <pthread.h> as well for typedefs of timespec/etc
+#else
+#error No clock_gettime(2) compatibility wrapper available for this platform.
+#endif
diff --git a/faux-folly/folly/detail/Clock.h b/faux-folly/folly/detail/Clock.h
new file mode 100644
index 0000000..de7130d
--- /dev/null
+++ b/faux-folly/folly/detail/Clock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_CLOCK_H_
+#define FOLLY_DETAIL_CLOCK_H_
+
+#include <ctime>
+#include <cstdint>
+
+#include <folly/Portability.h>
+
+#if FOLLY_HAVE_CLOCK_GETTIME
+#error This should only be used as a workaround for platforms \
+          that do not support clock_gettime(2).
+#endif
+
+/* For windows, we'll use pthread's time implementations */
+#ifdef _MSC_VER
+#include <pthread.h>
+#include <pthread_time.h>
+#else
+typedef uint8_t clockid_t;
+#define CLOCK_REALTIME 0
+
+int clock_gettime(clockid_t clk_id, struct timespec* ts);
+int clock_getres(clockid_t clk_id, struct timespec* ts);
+#endif
+
+#endif /* FOLLY_DETAIL_CLOCK_H_ */
diff --git a/faux-folly/folly/detail/ExceptionWrapper.h b/faux-folly/folly/detail/ExceptionWrapper.h
new file mode 100644
index 0000000..03d8190
--- /dev/null
+++ b/faux-folly/folly/detail/ExceptionWrapper.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_EXCEPTIONWRAPPER_H
+#define FOLLY_DETAIL_EXCEPTIONWRAPPER_H
+
+namespace folly { namespace detail {
+
+template <class T>
+class Thrower {
+ public:
+  static void doThrow(std::exception* obj) {
+    throw *static_cast<T*>(obj);
+  }
+};
+
+}}
+
+#endif
diff --git a/faux-folly/folly/detail/FileUtilDetail.h b/faux-folly/folly/detail/FileUtilDetail.h
new file mode 100644
index 0000000..10e9819
--- /dev/null
+++ b/faux-folly/folly/detail/FileUtilDetail.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_FILEUTILDETAIL_H_
+#define FOLLY_DETAIL_FILEUTILDETAIL_H_
+
+#include <cerrno>
+#include <unistd.h>
+
+#include <sys/uio.h>
+
+/**
+ * Helper functions and templates for FileUtil.cpp.  Declared here so
+ * they can be unittested.
+ */
+namespace folly { namespace fileutil_detail {
+
+// Wrap call to f(args) in loop to retry on EINTR
+template<class F, class... Args>
+ssize_t wrapNoInt(F f, Args... args) {
+  ssize_t r;
+  do {
+    r = f(args...);
+  } while (r == -1 && errno == EINTR);
+  return r;
+}
+
+inline void incr(ssize_t n) { }
+inline void incr(ssize_t n, off_t& offset) { offset += n; }
+
+// Wrap call to read/pread/write/pwrite(fd, buf, count, offset?) to retry on
+// incomplete reads / writes.  The variadic argument magic is there to support
+// an additional argument (offset) for pread / pwrite; see the incr() functions
+// above which do nothing if the offset is not present and increment it if it
+// is.
+template <class F, class... Offset>
+ssize_t wrapFull(F f, int fd, void* buf, size_t count, Offset... offset) {
+  char* b = static_cast<char*>(buf);
+  ssize_t totalBytes = 0;
+  ssize_t r;
+  do {
+    r = f(fd, b, count, offset...);
+    if (r == -1) {
+      if (errno == EINTR) {
+        continue;
+      }
+      return r;
+    }
+
+    totalBytes += r;
+    b += r;
+    count -= r;
+    incr(r, offset...);
+  } while (r != 0 && count);  // 0 means EOF
+
+  return totalBytes;
+}
+
+// Wrap call to readv/preadv/writev/pwritev(fd, iov, count, offset?) to
+// retry on incomplete reads / writes.
+template <class F, class... Offset>
+ssize_t wrapvFull(F f, int fd, iovec* iov, int count, Offset... offset) {
+  ssize_t totalBytes = 0;
+  size_t r;
+  do {
+    r = f(fd, iov, count, offset...);
+    if (r == (size_t)-1) {
+      if (errno == EINTR) {
+        continue;
+      }
+      return r;
+    }
+
+    if (r == 0) {
+      break;  // EOF
+    }
+
+    totalBytes += r;
+    incr(r, offset...);
+    while (r != 0 && count != 0) {
+      if (r >= iov->iov_len) {
+        r -= iov->iov_len;
+        ++iov;
+        --count;
+      } else {
+        iov->iov_base = static_cast<char*>(iov->iov_base) + r;
+        iov->iov_len -= r;
+        r = 0;
+      }
+    }
+  } while (count);
+
+  return totalBytes;
+}
+
+}}  // namespaces
+
+#endif /* FOLLY_DETAIL_FILEUTILDETAIL_H_ */
diff --git a/faux-folly/folly/detail/FunctionalExcept.cpp b/faux-folly/folly/detail/FunctionalExcept.cpp
new file mode 100644
index 0000000..b0427cf
--- /dev/null
+++ b/faux-folly/folly/detail/FunctionalExcept.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 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/Portability.h>
+
+// If FOLLY_HAVE_BITS_FUNCTEXCEPT_H is set, this file compiles to
+// nothing.
+
+#if !FOLLY_HAVE_BITS_FUNCTEXCEPT_H
+
+#include <folly/detail/FunctionalExcept.h>
+
+#include <stdexcept>
+
+FOLLY_NAMESPACE_STD_BEGIN
+
+void __throw_length_error(const char* msg) {
+  throw std::length_error(msg);
+}
+
+void __throw_logic_error(const char* msg) {
+  throw std::logic_error(msg);
+}
+
+void __throw_out_of_range(const char* msg) {
+  throw std::out_of_range(msg);
+}
+
+#if defined(_MSC_VER)
+void __throw_bad_alloc() {
+  throw std::bad_alloc();
+}
+#endif
+
+FOLLY_NAMESPACE_STD_END
+
+#endif
diff --git a/faux-folly/folly/detail/FunctionalExcept.h b/faux-folly/folly/detail/FunctionalExcept.h
new file mode 100644
index 0000000..a041378
--- /dev/null
+++ b/faux-folly/folly/detail/FunctionalExcept.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_FUNCTIONAL_EXCEPT_H
+#define FOLLY_DETAIL_FUNCTIONAL_EXCEPT_H
+
+#include <folly/Portability.h>
+
+#if !FOLLY_HAVE_BITS_FUNCTEXCEPT_H
+
+FOLLY_NAMESPACE_STD_BEGIN
+
+FOLLY_NORETURN void __throw_length_error(const char* msg);
+FOLLY_NORETURN void __throw_logic_error(const char* msg);
+FOLLY_NORETURN void __throw_out_of_range(const char* msg);
+
+#ifdef _MSC_VER
+FOLLY_NORETURN void __throw_bad_alloc();
+#endif
+
+FOLLY_NAMESPACE_STD_END
+
+#else
+#error This file should never be included if FOLLY_HAVE_BITS_FUNCTEXCEPT_H is set
+#endif
+
+#endif
diff --git a/faux-folly/folly/detail/Futex.cpp b/faux-folly/folly/detail/Futex.cpp
new file mode 100644
index 0000000..bef3c02
--- /dev/null
+++ b/faux-folly/folly/detail/Futex.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2015 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/detail/Futex.h>
+#include <stdint.h>
+#include <string.h>
+#include <condition_variable>
+#include <mutex>
+#include <boost/intrusive/list.hpp>
+#include <folly/Hash.h>
+#include <folly/ScopeGuard.h>
+
+#ifdef __linux__
+# include <errno.h>
+# include <linux/futex.h>
+# include <sys/syscall.h>
+#endif
+
+using namespace std::chrono;
+
+namespace folly { namespace detail {
+
+namespace {
+
+////////////////////////////////////////////////////
+// native implementation using the futex() syscall
+
+#ifdef __linux__
+
+/// Certain toolchains (like Android's) don't include the full futex API in
+/// their headers even though they support it. Make sure we have our constants
+/// even if the headers don't have them.
+#ifndef FUTEX_WAIT_BITSET
+# define FUTEX_WAIT_BITSET 9
+#endif
+#ifndef FUTEX_WAKE_BITSET
+# define FUTEX_WAKE_BITSET 10
+#endif
+#ifndef FUTEX_PRIVATE_FLAG
+# define FUTEX_PRIVATE_FLAG 128
+#endif
+#ifndef FUTEX_CLOCK_REALTIME
+# define FUTEX_CLOCK_REALTIME 256
+#endif
+
+int nativeFutexWake(void* addr, int count, uint32_t wakeMask) {
+  int rv = syscall(__NR_futex,
+                   addr, /* addr1 */
+                   FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, /* op */
+                   count, /* val */
+                   nullptr, /* timeout */
+                   nullptr, /* addr2 */
+                   wakeMask); /* val3 */
+
+  /* NOTE: we ignore errors on wake for the case of a futex
+     guarding its own destruction, similar to this
+     glibc bug with sem_post/sem_wait:
+     https://sourceware.org/bugzilla/show_bug.cgi?id=12674 */
+  if (rv < 0) {
+    return 0;
+  }
+  return rv;
+}
+
+template <class Clock>
+struct timespec
+timeSpecFromTimePoint(time_point<Clock> absTime)
+{
+  auto epoch = absTime.time_since_epoch();
+  if (epoch.count() < 0) {
+    // kernel timespec_valid requires non-negative seconds and nanos in [0,1G)
+    epoch = Clock::duration::zero();
+  }
+
+  // timespec-safe seconds and nanoseconds;
+  // chrono::{nano,}seconds are `long long int`
+  // whereas timespec uses smaller types
+  using time_t_seconds = duration<std::time_t, seconds::period>;
+  using long_nanos = duration<long int, nanoseconds::period>;
+
+  auto secs = duration_cast<time_t_seconds>(epoch);
+  auto nanos = duration_cast<long_nanos>(epoch - secs);
+  struct timespec result = { secs.count(), nanos.count() };
+  return result;
+}
+
+FutexResult nativeFutexWaitImpl(void* addr,
+                                uint32_t expected,
+                                time_point<system_clock>* absSystemTime,
+                                time_point<steady_clock>* absSteadyTime,
+                                uint32_t waitMask) {
+  assert(absSystemTime == nullptr || absSteadyTime == nullptr);
+
+  int op = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG;
+  struct timespec ts;
+  struct timespec* timeout = nullptr;
+
+  if (absSystemTime != nullptr) {
+    op |= FUTEX_CLOCK_REALTIME;
+    ts = timeSpecFromTimePoint(*absSystemTime);
+    timeout = &ts;
+  } else if (absSteadyTime != nullptr) {
+    ts = timeSpecFromTimePoint(*absSteadyTime);
+    timeout = &ts;
+  }
+
+  // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET requires an absolute timeout
+  // value - http://locklessinc.com/articles/futex_cheat_sheet/
+  int rv = syscall(__NR_futex,
+                   addr, /* addr1 */
+                   op, /* op */
+                   expected, /* val */
+                   timeout, /* timeout */
+                   nullptr, /* addr2 */
+                   waitMask); /* val3 */
+
+  if (rv == 0) {
+    return FutexResult::AWOKEN;
+  } else {
+    switch(errno) {
+      case ETIMEDOUT:
+        assert(timeout != nullptr);
+        return FutexResult::TIMEDOUT;
+      case EINTR:
+        return FutexResult::INTERRUPTED;
+      case EWOULDBLOCK:
+        return FutexResult::VALUE_CHANGED;
+      default:
+        assert(false);
+        // EINVAL, EACCESS, or EFAULT.  EINVAL means there was an invalid
+        // op (should be impossible) or an invalid timeout (should have
+        // been sanitized by timeSpecFromTimePoint).  EACCESS or EFAULT
+        // means *addr points to invalid memory, which is unlikely because
+        // the caller should have segfaulted already.  We can either
+        // crash, or return a value that lets the process continue for
+        // a bit. We choose the latter. VALUE_CHANGED probably turns the
+        // caller into a spin lock.
+        return FutexResult::VALUE_CHANGED;
+    }
+  }
+}
+
+#endif // __linux__
+
+///////////////////////////////////////////////////////
+// compatibility implementation using standard C++ API
+
+// Our emulated futex uses 4096 lists of wait nodes.  There are two levels
+// of locking: a per-list mutex that controls access to the list and a
+// per-node mutex, condvar, and bool that are used for the actual wakeups.
+// The per-node mutex allows us to do precise wakeups without thundering
+// herds.
+
+struct EmulatedFutexWaitNode : public boost::intrusive::list_base_hook<> {
+  void* const addr_;
+  const uint32_t waitMask_;
+
+  // tricky: hold both bucket and node mutex to write, either to read
+  bool signaled_;
+  std::mutex mutex_;
+  std::condition_variable cond_;
+
+  EmulatedFutexWaitNode(void* addr, uint32_t waitMask)
+    : addr_(addr)
+    , waitMask_(waitMask)
+    , signaled_(false)
+  {
+  }
+};
+
+struct EmulatedFutexBucket {
+  std::mutex mutex_;
+  boost::intrusive::list<EmulatedFutexWaitNode> waiters_;
+
+  static const size_t kNumBuckets = 4096;
+  static EmulatedFutexBucket* gBuckets;
+  static std::once_flag gBucketInit;
+
+  static EmulatedFutexBucket& bucketFor(void* addr) {
+    std::call_once(gBucketInit, [](){
+      gBuckets = new EmulatedFutexBucket[kNumBuckets];
+    });
+    uint64_t mixedBits = folly::hash::twang_mix64(
+        reinterpret_cast<uintptr_t>(addr));
+    return gBuckets[mixedBits % kNumBuckets];
+  }
+};
+
+EmulatedFutexBucket* EmulatedFutexBucket::gBuckets;
+std::once_flag EmulatedFutexBucket::gBucketInit;
+
+int emulatedFutexWake(void* addr, int count, uint32_t waitMask) {
+  auto& bucket = EmulatedFutexBucket::bucketFor(addr);
+  std::unique_lock<std::mutex> bucketLock(bucket.mutex_);
+
+  int numAwoken = 0;
+  for (auto iter = bucket.waiters_.begin();
+       numAwoken < count && iter != bucket.waiters_.end(); ) {
+    auto current = iter;
+    auto& node = *iter++;
+    if (node.addr_ == addr && (node.waitMask_ & waitMask) != 0) {
+      ++numAwoken;
+
+      // we unlink, but waiter destroys the node
+      bucket.waiters_.erase(current);
+
+      std::unique_lock<std::mutex> nodeLock(node.mutex_);
+      node.signaled_ = true;
+      node.cond_.notify_one();
+    }
+  }
+  return numAwoken;
+}
+
+FutexResult emulatedFutexWaitImpl(
+        void* addr,
+        uint32_t expected,
+        time_point<system_clock>* absSystemTime,
+        time_point<steady_clock>* absSteadyTime,
+        uint32_t waitMask) {
+  auto& bucket = EmulatedFutexBucket::bucketFor(addr);
+  EmulatedFutexWaitNode node(addr, waitMask);
+
+  {
+    std::unique_lock<std::mutex> bucketLock(bucket.mutex_);
+
+    uint32_t actual;
+    memcpy(&actual, addr, sizeof(uint32_t));
+    if (actual != expected) {
+      return FutexResult::VALUE_CHANGED;
+    }
+
+    bucket.waiters_.push_back(node);
+  } // bucketLock scope
+
+  std::cv_status status = std::cv_status::no_timeout;
+  {
+    std::unique_lock<std::mutex> nodeLock(node.mutex_);
+    while (!node.signaled_ && status != std::cv_status::timeout) {
+      if (absSystemTime != nullptr) {
+        status = node.cond_.wait_until(nodeLock, *absSystemTime);
+      } else if (absSteadyTime != nullptr) {
+        status = node.cond_.wait_until(nodeLock, *absSteadyTime);
+      } else {
+        node.cond_.wait(nodeLock);
+      }
+    }
+  } // nodeLock scope
+
+  if (status == std::cv_status::timeout) {
+    // it's not really a timeout until we unlink the unsignaled node
+    std::unique_lock<std::mutex> bucketLock(bucket.mutex_);
+    if (!node.signaled_) {
+      bucket.waiters_.erase(bucket.waiters_.iterator_to(node));
+      return FutexResult::TIMEDOUT;
+    }
+  }
+  return FutexResult::AWOKEN;
+}
+
+} // anon namespace
+
+
+/////////////////////////////////
+// Futex<> specializations
+
+template <>
+int
+Futex<std::atomic>::futexWake(int count, uint32_t wakeMask) {
+#ifdef __linux__
+  return nativeFutexWake(this, count, wakeMask);
+#else
+  return emulatedFutexWake(this, count, wakeMask);
+#endif
+}
+
+template <>
+int
+Futex<EmulatedFutexAtomic>::futexWake(int count, uint32_t wakeMask) {
+  return emulatedFutexWake(this, count, wakeMask);
+}
+
+template <>
+FutexResult
+Futex<std::atomic>::futexWaitImpl(uint32_t expected,
+                                  time_point<system_clock>* absSystemTime,
+                                  time_point<steady_clock>* absSteadyTime,
+                                  uint32_t waitMask) {
+#ifdef __linux__
+  return nativeFutexWaitImpl(
+      this, expected, absSystemTime, absSteadyTime, waitMask);
+#else
+  return emulatedFutexWaitImpl(
+      this, expected, absSystemTime, absSteadyTime, waitMask);
+#endif
+}
+
+template <>
+FutexResult
+Futex<EmulatedFutexAtomic>::futexWaitImpl(
+        uint32_t expected,
+        time_point<system_clock>* absSystemTime,
+        time_point<steady_clock>* absSteadyTime,
+        uint32_t waitMask) {
+  return emulatedFutexWaitImpl(
+      this, expected, absSystemTime, absSteadyTime, waitMask);
+}
+
+}} // namespace folly::detail
diff --git a/faux-folly/folly/detail/Futex.h b/faux-folly/folly/detail/Futex.h
new file mode 100644
index 0000000..3cd2c18
--- /dev/null
+++ b/faux-folly/folly/detail/Futex.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <chrono>
+#include <limits>
+#include <assert.h>
+#include <unistd.h>
+#include <boost/noncopyable.hpp>
+
+namespace folly { namespace detail {
+
+enum class FutexResult {
+  VALUE_CHANGED, /* Futex value didn't match expected */
+  AWOKEN,        /* futex wait matched with a futex wake */
+  INTERRUPTED,   /* Spurious wake-up or signal caused futex wait failure */
+  TIMEDOUT
+};
+
+/**
+ * Futex is an atomic 32 bit unsigned integer that provides access to the
+ * futex() syscall on that value.  It is templated in such a way that it
+ * can interact properly with DeterministicSchedule testing.
+ *
+ * If you don't know how to use futex(), you probably shouldn't be using
+ * this class.  Even if you do know how, you should have a good reason
+ * (and benchmarks to back you up).
+ */
+template <template <typename> class Atom = std::atomic>
+struct Futex : Atom<uint32_t>, boost::noncopyable {
+
+  explicit Futex(uint32_t init = 0) : Atom<uint32_t>(init) {}
+
+  /** Puts the thread to sleep if this->load() == expected.  Returns true when
+   *  it is returning because it has consumed a wake() event, false for any
+   *  other return (signal, this->load() != expected, or spurious wakeup). */
+  bool futexWait(uint32_t expected, uint32_t waitMask = -1) {
+    auto rv = futexWaitImpl(expected, nullptr, nullptr, waitMask);
+    assert(rv != FutexResult::TIMEDOUT);
+    return rv == FutexResult::AWOKEN;
+  }
+
+  /** Similar to futexWait but also accepts a timeout that gives the time until
+   *  when the call can block (time is the absolute time i.e time since epoch).
+   *  Allowed clock types: std::chrono::system_clock, std::chrono::steady_clock.
+   *  Returns one of FutexResult values.
+   *
+   *  NOTE: On some systems steady_clock is just an alias for system_clock,
+   *  and is not actually steady.*/
+  template <class Clock, class Duration = typename Clock::duration>
+  FutexResult futexWaitUntil(
+          uint32_t expected,
+          const std::chrono::time_point<Clock, Duration>& absTime,
+          uint32_t waitMask = -1) {
+    using std::chrono::duration_cast;
+    using std::chrono::nanoseconds;
+    using std::chrono::seconds;
+    using std::chrono::steady_clock;
+    using std::chrono::system_clock;
+    using std::chrono::time_point;
+
+    static_assert(
+        (std::is_same<Clock, system_clock>::value ||
+         std::is_same<Clock, steady_clock>::value),
+        "futexWaitUntil only knows std::chrono::{system_clock,steady_clock}");
+    assert((std::is_same<Clock, system_clock>::value) || Clock::is_steady);
+
+    // We launder the clock type via a std::chrono::duration so that we
+    // can compile both the true and false branch.  Tricky case is when
+    // steady_clock has a higher precision than system_clock (Xcode 6,
+    // for example), for which time_point<system_clock> construction
+    // refuses to do an implicit duration conversion.  (duration is
+    // happy to implicitly convert its denominator causing overflow, but
+    // refuses conversion that might cause truncation.)  We use explicit
+    // duration_cast to work around this.  Truncation does not actually
+    // occur (unless Duration != Clock::duration) because the missing
+    // implicit conversion is in the untaken branch.
+    Duration absTimeDuration = absTime.time_since_epoch();
+    if (std::is_same<Clock, system_clock>::value) {
+      time_point<system_clock> absSystemTime(
+          duration_cast<system_clock::duration>(absTimeDuration));
+      return futexWaitImpl(expected, &absSystemTime, nullptr, waitMask);
+    } else {
+      time_point<steady_clock> absSteadyTime(
+          duration_cast<steady_clock::duration>(absTimeDuration));
+      return futexWaitImpl(expected, nullptr, &absSteadyTime, waitMask);
+    }
+  }
+
+  /** Wakens up to count waiters where (waitMask & wakeMask) !=
+   *  0, returning the number of awoken threads, or -1 if an error
+   *  occurred.  Note that when constructing a concurrency primitive
+   *  that can guard its own destruction, it is likely that you will
+   *  want to ignore EINVAL here (as well as making sure that you
+   *  never touch the object after performing the memory store that
+   *  is the linearization point for unlock or control handoff).
+   *  See https://sourceware.org/bugzilla/show_bug.cgi?id=13690 */
+  int futexWake(int count = std::numeric_limits<int>::max(),
+                uint32_t wakeMask = -1);
+
+ private:
+
+  /** Underlying implementation of futexWait and futexWaitUntil.
+   *  At most one of absSystemTime and absSteadyTime should be non-null.
+   *  Timeouts are separated into separate parameters to allow the
+   *  implementations to be elsewhere without templating on the clock
+   *  type, which is otherwise complicated by the fact that steady_clock
+   *  is the same as system_clock on some platforms. */
+  FutexResult futexWaitImpl(
+      uint32_t expected,
+      std::chrono::time_point<std::chrono::system_clock>* absSystemTime,
+      std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime,
+      uint32_t waitMask);
+};
+
+/** A std::atomic subclass that can be used to force Futex to emulate
+ *  the underlying futex() syscall.  This is primarily useful to test or
+ *  benchmark the emulated implementation on systems that don't need it. */
+template <typename T>
+struct EmulatedFutexAtomic : public std::atomic<T> {
+  EmulatedFutexAtomic() noexcept = default;
+  constexpr /* implicit */ EmulatedFutexAtomic(T init) noexcept
+      : std::atomic<T>(init) {}
+  // It doesn't copy or move
+  EmulatedFutexAtomic(EmulatedFutexAtomic&& rhs) = delete;
+};
+
+/* Available specializations, with definitions elsewhere */
+
+template<>
+int Futex<std::atomic>::futexWake(int count, uint32_t wakeMask);
+
+template<>
+FutexResult Futex<std::atomic>::futexWaitImpl(
+      uint32_t expected,
+      std::chrono::time_point<std::chrono::system_clock>* absSystemTime,
+      std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime,
+      uint32_t waitMask);
+
+template<>
+int Futex<EmulatedFutexAtomic>::futexWake(int count, uint32_t wakeMask);
+
+template<>
+FutexResult Futex<EmulatedFutexAtomic>::futexWaitImpl(
+      uint32_t expected,
+      std::chrono::time_point<std::chrono::system_clock>* absSystemTime,
+      std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime,
+      uint32_t waitMask);
+
+}}
diff --git a/faux-folly/folly/detail/Malloc.h b/faux-folly/folly/detail/Malloc.h
new file mode 100644
index 0000000..e6d1e49
--- /dev/null
+++ b/faux-folly/folly/detail/Malloc.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_MALLOC_H
+#define FOLLY_DETAIL_MALLOC_H
+
+#include <stdlib.h>
+
+#include <folly/Portability.h>
+
+extern "C" {
+
+#if FOLLY_HAVE_WEAK_SYMBOLS
+void* mallocx(size_t, int) __attribute__((__weak__));
+void* rallocx(void*, size_t, int) __attribute__((__weak__));
+size_t xallocx(void*, size_t, size_t, int) __attribute__((__weak__));
+size_t sallocx(const void*, int) __attribute__((__weak__));
+void dallocx(void*, int) __attribute__((__weak__));
+size_t nallocx(size_t, int) __attribute__((__weak__));
+int mallctl(const char*, void*, size_t*, void*, size_t)
+      __attribute__((__weak__));
+int mallctlnametomib(const char*, size_t*, size_t*) __attribute__((__weak__));
+int mallctlbymib(const size_t*, size_t, void*, size_t*, void*, size_t)
+      __attribute__((__weak__));
+#else
+extern void* (*mallocx)(size_t, int);
+extern void* (*rallocx)(void*, size_t, int);
+extern size_t (*xallocx)(void*, size_t, size_t, int);
+extern size_t (*sallocx)(const void*, int);
+extern void (*dallocx)(void*, int);
+extern size_t (*nallocx)(size_t, int);
+extern int (*mallctl)(const char*, void*, size_t*, void*, size_t);
+extern int (*mallctlnametomib)(const char*, size_t*, size_t*);
+extern int (*mallctlbymib)(const size_t*, size_t, void*, size_t*, void*,
+                           size_t);
+#endif
+
+}
+
+#endif
diff --git a/faux-folly/folly/detail/MallocImpl.cpp b/faux-folly/folly/detail/MallocImpl.cpp
new file mode 100644
index 0000000..8a83562
--- /dev/null
+++ b/faux-folly/folly/detail/MallocImpl.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 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/detail/Malloc.h>
+
+extern "C" {
+
+#if !FOLLY_HAVE_WEAK_SYMBOLS
+void* (*mallocx)(size_t, int) = nullptr;
+void* (*rallocx)(void*, size_t, int) = nullptr;
+size_t (*xallocx)(void*, size_t, size_t, int) = nullptr;
+size_t (*sallocx)(const void*, int) = nullptr;
+void (*dallocx)(void*, int) = nullptr;
+size_t (*nallocx)(size_t, int) = nullptr;
+int (*mallctl)(const char*, void*, size_t*, void*, size_t) = nullptr;
+int (*mallctlnametomib)(const char*, size_t*, size_t*) = nullptr;
+int (*mallctlbymib)(const size_t*, size_t, void*, size_t*, void*, size_t) =
+    nullptr;
+#endif
+
+}
diff --git a/faux-folly/folly/detail/MemoryIdler.cpp b/faux-folly/folly/detail/MemoryIdler.cpp
new file mode 100644
index 0000000..fcba197
--- /dev/null
+++ b/faux-folly/folly/detail/MemoryIdler.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2015 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/detail/MemoryIdler.h>
+#include <folly/Logging.h>
+#include <folly/Malloc.h>
+#include <folly/ScopeGuard.h>
+#include <folly/detail/CacheLocality.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <utility>
+
+
+namespace folly { namespace detail {
+
+AtomicStruct<std::chrono::steady_clock::duration>
+MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5));
+
+
+/// Calls mallctl, optionally reading and/or writing an unsigned value
+/// if in and/or out is non-null.  Logs on error
+static unsigned mallctlWrapper(const char* cmd, const unsigned* in,
+                               unsigned* out) {
+  size_t outLen = sizeof(unsigned);
+  int err = mallctl(cmd,
+                    out, out ? &outLen : nullptr,
+                    const_cast<unsigned*>(in), in ? sizeof(unsigned) : 0);
+  if (err != 0) {
+    FB_LOG_EVERY_MS(WARNING, 10000)
+      << "mallctl " << cmd << ": " << strerror(err) << " (" << err << ")";
+  }
+  return err;
+}
+
+void MemoryIdler::flushLocalMallocCaches() {
+  if (usingJEMalloc()) {
+    if (!mallctl || !mallctlnametomib || !mallctlbymib) {
+      FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed";
+      return;
+    }
+
+    // "tcache.flush" was renamed to "thread.tcache.flush" in jemalloc 3
+    (void)mallctlWrapper("thread.tcache.flush", nullptr, nullptr);
+
+    // By default jemalloc has 4 arenas per cpu, and then assigns each
+    // thread to one of those arenas.  This means that in any service
+    // that doesn't perform a lot of context switching, the chances that
+    // another thread will be using the current thread's arena (and hence
+    // doing the appropriate dirty-page purging) are low.  Some good
+    // tuned configurations (such as that used by hhvm) use fewer arenas
+    // and then pin threads to avoid contended access.  In that case,
+    // purging the arenas is counter-productive.  We use the heuristic
+    // that if narenas <= 2 * num_cpus then we shouldn't do anything here,
+    // which detects when the narenas has been reduced from the default
+    unsigned narenas, arenaForCurrent;
+    size_t mib[3];
+    size_t miblen = 3;
+    if (mallctlWrapper("opt.narenas", nullptr, &narenas) == 0 &&
+        narenas > 2 * CacheLocality::system().numCpus &&
+        mallctlWrapper("thread.arena", nullptr, &arenaForCurrent) == 0 &&
+        mallctlnametomib("arena.0.purge", mib, &miblen) == 0) {
+      mib[1] = size_t(arenaForCurrent);
+      mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);
+    }
+  }
+}
+
+
+// Stack madvise isn't Linux or glibc specific, but the system calls
+// and arithmetic (and bug compatibility) are not portable.  The set of
+// platforms could be increased if it was useful.
+#if FOLLY_X64 && defined(_GNU_SOURCE) && defined(__linux__)
+
+static const size_t s_pageSize = sysconf(_SC_PAGESIZE);
+static FOLLY_TLS uintptr_t tls_stackLimit;
+static FOLLY_TLS size_t tls_stackSize;
+
+static void fetchStackLimits() {
+  pthread_attr_t attr;
+  pthread_getattr_np(pthread_self(), &attr);
+  SCOPE_EXIT { pthread_attr_destroy(&attr); };
+
+  void* addr;
+  size_t rawSize;
+  int err;
+  if ((err = pthread_attr_getstack(&attr, &addr, &rawSize))) {
+    // unexpected, but it is better to continue in prod than do nothing
+    FB_LOG_EVERY_MS(ERROR, 10000) << "pthread_attr_getstack error " << err;
+    assert(false);
+    tls_stackSize = 1;
+    return;
+  }
+  assert(addr != nullptr);
+  assert(rawSize >= PTHREAD_STACK_MIN);
+
+  // glibc subtracts guard page from stack size, even though pthread docs
+  // seem to imply the opposite
+  size_t guardSize;
+  if (pthread_attr_getguardsize(&attr, &guardSize) != 0) {
+    guardSize = 0;
+  }
+  assert(rawSize > guardSize);
+
+  // stack goes down, so guard page adds to the base addr
+  tls_stackLimit = uintptr_t(addr) + guardSize;
+  tls_stackSize = rawSize - guardSize;
+
+  assert((tls_stackLimit & (s_pageSize - 1)) == 0);
+}
+
+FOLLY_NOINLINE static uintptr_t getStackPtr() {
+  char marker;
+  auto rv = uintptr_t(&marker);
+  return rv;
+}
+
+void MemoryIdler::unmapUnusedStack(size_t retain) {
+  if (tls_stackSize == 0) {
+    fetchStackLimits();
+  }
+  if (tls_stackSize <= std::max(size_t(1), retain)) {
+    // covers both missing stack info, and impossibly large retain
+    return;
+  }
+
+  auto sp = getStackPtr();
+  assert(sp >= tls_stackLimit);
+  assert(sp - tls_stackLimit < tls_stackSize);
+
+  auto end = (sp - retain) & ~(s_pageSize - 1);
+  if (end <= tls_stackLimit) {
+    // no pages are eligible for unmapping
+    return;
+  }
+
+  size_t len = end - tls_stackLimit;
+  assert((len & (s_pageSize - 1)) == 0);
+  if (madvise((void*)tls_stackLimit, len, MADV_DONTNEED) != 0) {
+    // It is likely that the stack vma hasn't been fully grown.  In this
+    // case madvise will apply dontneed to the present vmas, then return
+    // errno of ENOMEM.  We can also get an EAGAIN, theoretically.
+    // EINVAL means either an invalid alignment or length, or that some
+    // of the pages are locked or shared.  Neither should occur.
+    assert(errno == EAGAIN || errno == ENOMEM);
+  }
+}
+
+#else
+
+void MemoryIdler::unmapUnusedStack(size_t retain) {
+}
+
+#endif
+
+}}
diff --git a/faux-folly/folly/detail/MemoryIdler.h b/faux-folly/folly/detail/MemoryIdler.h
new file mode 100644
index 0000000..fa168e3
--- /dev/null
+++ b/faux-folly/folly/detail/MemoryIdler.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_MEMORYIDLER_H
+#define FOLLY_DETAIL_MEMORYIDLER_H
+
+#include <atomic>
+#include <chrono>
+#include <folly/AtomicStruct.h>
+#include <folly/Hash.h>
+#include <folly/Traits.h>
+#include <folly/detail/Futex.h>
+
+namespace folly {
+
+// gcc 4.7 doesn't do std::is_trivial correctly, override so we can use
+// AtomicStruct<duration>
+template<>
+struct IsTriviallyCopyable<std::chrono::steady_clock::duration>
+  : std::true_type {};
+
+}
+
+namespace folly { namespace detail {
+
+/// MemoryIdler provides helper routines that allow routines to return
+/// some assigned memory resources back to the system.  The intended
+/// use is that when a thread is waiting for a long time (perhaps it
+/// is in a LIFO thread pool and hasn't been needed for a long time)
+/// it should release its thread-local malloc caches (both jemalloc and
+/// tcmalloc use these for better performance) and unmap the stack pages
+/// that contain no useful data.
+struct MemoryIdler {
+
+  /// Returns memory from thread-local allocation pools to the global
+  /// pool, if we know how to for the current malloc implementation.
+  /// jemalloc is supported.
+  static void flushLocalMallocCaches();
+
+
+  enum {
+    /// This value is a tradeoff between reclaiming memory and triggering
+    /// a page fault immediately on wakeup.  Note that the actual unit
+    /// of idling for the stack is pages, so the actual stack that
+    /// will be available on wakeup without a page fault is between
+    /// kDefaultStackToRetain and kDefaultStackToRetain + PageSize -
+    /// 1 bytes.
+    kDefaultStackToRetain = 1024,
+  };
+
+  /// Uses madvise to discard the portion of the thread's stack that
+  /// currently doesn't hold any data, trying to ensure that no page
+  /// faults will occur during the next retain bytes of stack allocation
+  static void unmapUnusedStack(size_t retain = kDefaultStackToRetain);
+
+
+  /// The system-wide default for the amount of time a blocking
+  /// thread should wait before reclaiming idle memory.  Set this to
+  /// Duration::max() to never wait.  The default value is 5 seconds.
+  /// Endpoints using this idle timeout might randomly wait longer to
+  /// avoid synchronizing their flushes.
+  static AtomicStruct<std::chrono::steady_clock::duration> defaultIdleTimeout;
+
+  /// Selects a timeout pseudo-randomly chosen to be between
+  /// idleTimeout and idleTimeout * (1 + timeoutVariationFraction), to
+  /// smooth out the behavior in a bursty system
+  template <typename Clock = std::chrono::steady_clock>
+  static typename Clock::duration getVariationTimeout(
+      typename Clock::duration idleTimeout
+          = defaultIdleTimeout.load(std::memory_order_acquire),
+      float timeoutVariationFrac = 0.5) {
+    if (idleTimeout.count() > 0 && timeoutVariationFrac > 0) {
+      // hash the pthread_t and the time to get the adjustment.
+      // Standard hash func isn't very good, so bit mix the result
+      auto pr = std::make_pair(pthread_self(),
+                               Clock::now().time_since_epoch().count());
+      std::hash<decltype(pr)> hash_fn;
+      uint64_t h = folly::hash::twang_mix64(hash_fn(pr));
+
+      // multiplying the duration by a floating point doesn't work, grr..
+      auto extraFrac =
+        timeoutVariationFrac / std::numeric_limits<uint64_t>::max() * h;
+      uint64_t tics = idleTimeout.count() * (1 + extraFrac);
+      idleTimeout = typename Clock::duration(tics);
+    }
+
+    return idleTimeout;
+  }
+
+  /// Equivalent to fut.futexWait(expected, waitMask), but calls
+  /// flushLocalMallocCaches() and unmapUnusedStack(stackToRetain)
+  /// after idleTimeout has passed (if it has passed).  Internally uses
+  /// fut.futexWait and fut.futexWaitUntil.  Like futexWait, returns
+  /// false if interrupted with a signal.  The actual timeout will be
+  /// pseudo-randomly chosen to be between idleTimeout and idleTimeout *
+  /// (1 + timeoutVariationFraction), to smooth out the behavior in a
+  /// system with bursty requests.  The default is to wait up to 50%
+  /// extra, so on average 25% extra
+  template <template <typename> class Atom,
+            typename Clock = std::chrono::steady_clock>
+  static bool futexWait(
+      Futex<Atom>& fut,
+      uint32_t expected,
+      uint32_t waitMask = -1,
+      typename Clock::duration idleTimeout
+          = defaultIdleTimeout.load(std::memory_order_acquire),
+      size_t stackToRetain = kDefaultStackToRetain,
+      float timeoutVariationFrac = 0.5) {
+
+    if (idleTimeout == Clock::duration::max()) {
+      // no need to use futexWaitUntil if no timeout is possible
+      return fut.futexWait(expected, waitMask);
+    }
+
+    idleTimeout = getVariationTimeout(idleTimeout, timeoutVariationFrac);
+    if (idleTimeout.count() > 0) {
+      while (true) {
+        auto rv = fut.futexWaitUntil(
+          expected, Clock::now() + idleTimeout, waitMask);
+        if (rv == FutexResult::TIMEDOUT) {
+          // timeout is over
+          break;
+        }
+        // finished before timeout hit, no flush
+        assert(rv == FutexResult::VALUE_CHANGED || rv == FutexResult::AWOKEN ||
+               rv == FutexResult::INTERRUPTED);
+        return rv == FutexResult::AWOKEN;
+      }
+    }
+
+    // flush, then wait with no timeout
+    flushLocalMallocCaches();
+    unmapUnusedStack(stackToRetain);
+    return fut.futexWait(expected, waitMask);
+  }
+};
+
+}} // namespace folly::detail
+
+#endif
diff --git a/faux-folly/folly/detail/Sleeper.h b/faux-folly/folly/detail/Sleeper.h
new file mode 100644
index 0000000..cdd121e
--- /dev/null
+++ b/faux-folly/folly/detail/Sleeper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 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
+
+/*
+ * @author Keith Adams <kma@fb.com>
+ * @author Jordan DeLong <delong.j@fb.com>
+ */
+
+#include <cinttypes>
+#include <ctime>
+
+#include <folly/Portability.h>
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+namespace detail {
+
+  /*
+   * A helper object for the contended case. Starts off with eager
+   * spinning, and falls back to sleeping for small quantums.
+   */
+  class Sleeper {
+    static const uint32_t kMaxActiveSpin = 4000;
+
+    uint32_t spinCount;
+
+  public:
+    Sleeper() : spinCount(0) {}
+
+    void wait() {
+      if (spinCount < kMaxActiveSpin) {
+        ++spinCount;
+        asm_volatile_pause();
+      } else {
+        /*
+         * Always sleep 0.5ms, assuming this will make the kernel put
+         * us down for whatever its minimum timer resolution is (in
+         * linux this varies by kernel version from 1ms to 10ms).
+         */
+        struct timespec ts = { 0, 500000 };
+        nanosleep(&ts, nullptr);
+      }
+    }
+  };
+
+}
+}
diff --git a/faux-folly/folly/detail/Stats.h b/faux-folly/folly/detail/Stats.h
new file mode 100644
index 0000000..e5e6b65
--- /dev/null
+++ b/faux-folly/folly/detail/Stats.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_STATS_H_
+#define FOLLY_DETAIL_STATS_H_
+
+#include <chrono>
+#include <cstdint>
+#include <type_traits>
+
+namespace folly { namespace detail {
+
+/*
+ * Helper function to compute the average, given a specified input type and
+ * return type.
+ */
+
+// If the input is long double, divide using long double to avoid losing
+// precision.
+template <typename ReturnType>
+ReturnType avgHelper(long double sum, uint64_t count) {
+  if (count == 0) { return ReturnType(0); }
+  const long double countf = count;
+  return static_cast<ReturnType>(sum / countf);
+}
+
+// In all other cases divide using double precision.
+// This should be relatively fast, and accurate enough for most use cases.
+template <typename ReturnType, typename ValueType>
+typename std::enable_if<!std::is_same<typename std::remove_cv<ValueType>::type,
+                                      long double>::value,
+                        ReturnType>::type
+avgHelper(ValueType sum, uint64_t count) {
+  if (count == 0) { return ReturnType(0); }
+  const double sumf = sum;
+  const double countf = count;
+  return static_cast<ReturnType>(sumf / countf);
+}
+
+/*
+ * Helper function to compute the rate per Interval,
+ * given the specified count recorded over the elapsed time period.
+ */
+template <typename ReturnType=double,
+          typename TimeType=std::chrono::seconds,
+          typename Interval=TimeType>
+ReturnType rateHelper(ReturnType count, TimeType elapsed) {
+  if (elapsed == TimeType(0)) {
+    return 0;
+  }
+
+  // Use std::chrono::duration_cast to convert between the native
+  // duration and the desired interval.  However, convert the rates,
+  // rather than just converting the elapsed duration.  Converting the
+  // elapsed time first may collapse it down to 0 if the elapsed interval
+  // is less than the desired interval, which will incorrectly result in
+  // an infinite rate.
+  typedef std::chrono::duration<
+      ReturnType, std::ratio<TimeType::period::den,
+                             TimeType::period::num>> NativeRate;
+  typedef std::chrono::duration<
+      ReturnType, std::ratio<Interval::period::den,
+                             Interval::period::num>> DesiredRate;
+
+  NativeRate native(count / elapsed.count());
+  DesiredRate desired = std::chrono::duration_cast<DesiredRate>(native);
+  return desired.count();
+}
+
+
+template<typename T>
+struct Bucket {
+ public:
+  typedef T ValueType;
+
+  Bucket()
+    : sum(ValueType()),
+      count(0) {}
+
+  void clear() {
+    sum = ValueType();
+    count = 0;
+  }
+
+  void add(const ValueType& s, uint64_t c) {
+    // TODO: It would be nice to handle overflow here.
+    sum += s;
+    count += c;
+  }
+
+  Bucket& operator+=(const Bucket& o) {
+    add(o.sum, o.count);
+    return *this;
+  }
+
+  Bucket& operator-=(const Bucket& o) {
+    // TODO: It would be nice to handle overflow here.
+    sum -= o.sum;
+    count -= o.count;
+    return *this;
+  }
+
+  template <typename ReturnType>
+  ReturnType avg() const {
+    return avgHelper<ReturnType>(sum, count);
+  }
+
+  ValueType sum;
+  uint64_t count;
+};
+
+}} // folly::detail
+
+#endif // FOLLY_DETAIL_STATS_H_
diff --git a/faux-folly/folly/detail/ThreadLocalDetail.h b/faux-folly/folly/detail/ThreadLocalDetail.h
new file mode 100644
index 0000000..bd8658c
--- /dev/null
+++ b/faux-folly/folly/detail/ThreadLocalDetail.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_THREADLOCALDETAIL_H_
+#define FOLLY_DETAIL_THREADLOCALDETAIL_H_
+
+#include <limits.h>
+#include <pthread.h>
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include <folly/Foreach.h>
+#include <folly/Exception.h>
+#include <folly/Malloc.h>
+
+// In general, emutls cleanup is not guaranteed to play nice with the way
+// StaticMeta mixes direct pthread calls and the use of __thread. This has
+// caused problems on multiple platforms so don't use __thread there.
+//
+// XXX: Ideally we would instead determine if emutls is in use at runtime as it
+// is possible to configure glibc on Linux to use emutls regardless.
+#if !__APPLE__ && !__ANDROID__
+#define FOLLY_TLD_USE_FOLLY_TLS 1
+#else
+#undef FOLLY_TLD_USE_FOLLY_TLS
+#endif
+
+namespace folly {
+namespace threadlocal_detail {
+
+/**
+ * Base class for deleters.
+ */
+class DeleterBase {
+ public:
+  virtual ~DeleterBase() { }
+  virtual void dispose(void* ptr, TLPDestructionMode mode) const = 0;
+};
+
+/**
+ * Simple deleter class that calls delete on the passed-in pointer.
+ */
+template <class Ptr>
+class SimpleDeleter : public DeleterBase {
+ public:
+  virtual void dispose(void* ptr, TLPDestructionMode /*mode*/) const {
+    delete static_cast<Ptr>(ptr);
+  }
+};
+
+/**
+ * Custom deleter that calls a given callable.
+ */
+template <class Ptr, class Deleter>
+class CustomDeleter : public DeleterBase {
+ public:
+  explicit CustomDeleter(Deleter d) : deleter_(d) { }
+  virtual void dispose(void* ptr, TLPDestructionMode mode) const {
+    deleter_(static_cast<Ptr>(ptr), mode);
+  }
+ private:
+  Deleter deleter_;
+};
+
+
+/**
+ * POD wrapper around an element (a void*) and an associated deleter.
+ * This must be POD, as we memset() it to 0 and memcpy() it around.
+ */
+struct ElementWrapper {
+  bool dispose(TLPDestructionMode mode) {
+    if (ptr == nullptr) {
+      return false;
+    }
+
+    DCHECK(deleter != nullptr);
+    deleter->dispose(ptr, mode);
+    cleanup();
+    return true;
+  }
+
+  void* release() {
+    auto retPtr = ptr;
+
+    if (ptr != nullptr) {
+      cleanup();
+    }
+
+    return retPtr;
+  }
+
+  template <class Ptr>
+  void set(Ptr p) {
+    DCHECK(ptr == nullptr);
+    DCHECK(deleter == nullptr);
+
+    if (p) {
+      // We leak a single object here but that is ok.  If we used an
+      // object directly, there is a chance that the destructor will be
+      // called on that static object before any of the ElementWrappers
+      // are disposed and that isn't so nice.
+      static auto d = new SimpleDeleter<Ptr>();
+      ptr = p;
+      deleter = d;
+      ownsDeleter = false;
+    }
+  }
+
+  template <class Ptr, class Deleter>
+  void set(Ptr p, Deleter d) {
+    DCHECK(ptr == nullptr);
+    DCHECK(deleter == nullptr);
+    if (p) {
+      ptr = p;
+      deleter = new CustomDeleter<Ptr,Deleter>(d);
+      ownsDeleter = true;
+    }
+  }
+
+  void cleanup() {
+    if (ownsDeleter) {
+      delete deleter;
+    }
+    ptr = nullptr;
+    deleter = nullptr;
+    ownsDeleter = false;
+  }
+
+  void* ptr;
+  DeleterBase* deleter;
+  bool ownsDeleter;
+};
+
+/**
+ * Per-thread entry.  Each thread using a StaticMeta object has one.
+ * This is written from the owning thread only (under the lock), read
+ * from the owning thread (no lock necessary), and read from other threads
+ * (under the lock).
+ */
+struct ThreadEntry {
+  ElementWrapper* elements;
+  size_t elementsCapacity;
+  ThreadEntry* next;
+  ThreadEntry* prev;
+};
+
+// Held in a singleton to track our global instances.
+// We have one of these per "Tag", by default one for the whole system
+// (Tag=void).
+//
+// Creating and destroying ThreadLocalPtr objects, as well as thread exit
+// for threads that use ThreadLocalPtr objects collide on a lock inside
+// StaticMeta; you can specify multiple Tag types to break that lock.
+template <class Tag>
+struct StaticMeta {
+  static StaticMeta<Tag>& instance() {
+    // Leak it on exit, there's only one per process and we don't have to
+    // worry about synchronization with exiting threads.
+    static bool constructed = (inst_ = new StaticMeta<Tag>());
+    (void)constructed; // suppress unused warning
+    return *inst_;
+  }
+
+  uint32_t nextId_;
+  std::vector<uint32_t> freeIds_;
+  std::mutex lock_;
+  pthread_key_t pthreadKey_;
+  ThreadEntry head_;
+
+  void push_back(ThreadEntry* t) {
+    t->next = &head_;
+    t->prev = head_.prev;
+    head_.prev->next = t;
+    head_.prev = t;
+  }
+
+  void erase(ThreadEntry* t) {
+    t->next->prev = t->prev;
+    t->prev->next = t->next;
+    t->next = t->prev = t;
+  }
+
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+  static FOLLY_TLS ThreadEntry threadEntry_;
+#endif
+  static StaticMeta<Tag>* inst_;
+
+  StaticMeta() : nextId_(1) {
+    head_.next = head_.prev = &head_;
+    int ret = pthread_key_create(&pthreadKey_, &onThreadExit);
+    checkPosixError(ret, "pthread_key_create failed");
+
+#if FOLLY_HAVE_PTHREAD_ATFORK
+    ret = pthread_atfork(/*prepare*/ &StaticMeta::preFork,
+                         /*parent*/ &StaticMeta::onForkParent,
+                         /*child*/ &StaticMeta::onForkChild);
+    checkPosixError(ret, "pthread_atfork failed");
+#elif !__ANDROID__ && !defined(_MSC_VER)
+    // pthread_atfork is not part of the Android NDK at least as of n9d. If
+    // something is trying to call native fork() directly at all with Android's
+    // process management model, this is probably the least of the problems.
+    //
+    // But otherwise, this is a problem.
+    #warning pthread_atfork unavailable
+#endif
+  }
+  ~StaticMeta() {
+    LOG(FATAL) << "StaticMeta lives forever!";
+  }
+
+  static ThreadEntry* getThreadEntry() {
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+    return &threadEntry_;
+#else
+    ThreadEntry* threadEntry =
+        static_cast<ThreadEntry*>(pthread_getspecific(inst_->pthreadKey_));
+    if (!threadEntry) {
+        threadEntry = new ThreadEntry();
+        int ret = pthread_setspecific(inst_->pthreadKey_, threadEntry);
+        checkPosixError(ret, "pthread_setspecific failed");
+    }
+    return threadEntry;
+#endif
+  }
+
+  static void preFork(void) {
+    instance().lock_.lock();  // Make sure it's created
+  }
+
+  static void onForkParent(void) {
+    inst_->lock_.unlock();
+  }
+
+  static void onForkChild(void) {
+    // only the current thread survives
+    inst_->head_.next = inst_->head_.prev = &inst_->head_;
+    ThreadEntry* threadEntry = getThreadEntry();
+    // If this thread was in the list before the fork, add it back.
+    if (threadEntry->elementsCapacity != 0) {
+      inst_->push_back(threadEntry);
+    }
+    inst_->lock_.unlock();
+  }
+
+  static void onThreadExit(void* ptr) {
+    auto& meta = instance();
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+    ThreadEntry* threadEntry = getThreadEntry();
+
+    DCHECK_EQ(ptr, &meta);
+    DCHECK_GT(threadEntry->elementsCapacity, 0);
+#else
+    // pthread sets the thread-specific value corresponding
+    // to meta.pthreadKey_ to NULL before calling onThreadExit.
+    // We need to set it back to ptr to enable the correct behaviour
+    // of the subsequent calls of getThreadEntry
+    // (which may happen in user-provided custom deleters)
+    pthread_setspecific(meta.pthreadKey_, ptr);
+    ThreadEntry* threadEntry = static_cast<ThreadEntry*>(ptr);
+#endif
+    {
+      std::lock_guard<std::mutex> g(meta.lock_);
+      meta.erase(threadEntry);
+      // No need to hold the lock any longer; the ThreadEntry is private to this
+      // thread now that it's been removed from meta.
+    }
+    // NOTE: User-provided deleter / object dtor itself may be using ThreadLocal
+    // with the same Tag, so dispose() calls below may (re)create some of the
+    // elements or even increase elementsCapacity, thus multiple cleanup rounds
+    // may be required.
+    for (bool shouldRun = true; shouldRun; ) {
+      shouldRun = false;
+      FOR_EACH_RANGE(i, 0, threadEntry->elementsCapacity) {
+        if (threadEntry->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {
+          shouldRun = true;
+        }
+      }
+    }
+    free(threadEntry->elements);
+    threadEntry->elements = nullptr;
+    pthread_setspecific(meta.pthreadKey_, nullptr);
+
+#ifndef FOLLY_TLD_USE_FOLLY_TLS
+    // Allocated in getThreadEntry() when not using folly TLS; free it
+    delete threadEntry;
+#endif
+  }
+
+  static uint32_t create() {
+    uint32_t id;
+    auto & meta = instance();
+    std::lock_guard<std::mutex> g(meta.lock_);
+    if (!meta.freeIds_.empty()) {
+      id = meta.freeIds_.back();
+      meta.freeIds_.pop_back();
+    } else {
+      id = meta.nextId_++;
+    }
+    return id;
+  }
+
+  static void destroy(uint32_t id) {
+    try {
+      auto & meta = instance();
+      // Elements in other threads that use this id.
+      std::vector<ElementWrapper> elements;
+      {
+        std::lock_guard<std::mutex> g(meta.lock_);
+        for (ThreadEntry* e = meta.head_.next; e != &meta.head_; e = e->next) {
+          if (id < e->elementsCapacity && e->elements[id].ptr) {
+            elements.push_back(e->elements[id]);
+
+            /*
+             * Writing another thread's ThreadEntry from here is fine;
+             * the only other potential reader is the owning thread --
+             * from onThreadExit (which grabs the lock, so is properly
+             * synchronized with us) or from get(), which also grabs
+             * the lock if it needs to resize the elements vector.
+             *
+             * We can't conflict with reads for a get(id), because
+             * it's illegal to call get on a thread local that's
+             * destructing.
+             */
+            e->elements[id].ptr = nullptr;
+            e->elements[id].deleter = nullptr;
+            e->elements[id].ownsDeleter = false;
+          }
+        }
+        meta.freeIds_.push_back(id);
+      }
+      // Delete elements outside the lock
+      FOR_EACH(it, elements) {
+        it->dispose(TLPDestructionMode::ALL_THREADS);
+      }
+    } catch (...) { // Just in case we get a lock error or something anyway...
+      LOG(WARNING) << "Destructor discarding an exception that was thrown.";
+    }
+  }
+
+  /**
+   * Reserve enough space in the ThreadEntry::elements for the item
+   * @id to fit in.
+   */
+  static void reserve(uint32_t id) {
+    auto& meta = instance();
+    ThreadEntry* threadEntry = getThreadEntry();
+    size_t prevCapacity = threadEntry->elementsCapacity;
+    // Growth factor < 2, see folly/docs/FBVector.md; + 5 to prevent
+    // very slow start.
+    size_t newCapacity = static_cast<size_t>((id + 5) * 1.7);
+    assert(newCapacity > prevCapacity);
+    ElementWrapper* reallocated = nullptr;
+
+    // Need to grow. Note that we can't call realloc, as elements is
+    // still linked in meta, so another thread might access invalid memory
+    // after realloc succeeds. We'll copy by hand and update our ThreadEntry
+    // under the lock.
+    if (usingJEMalloc()) {
+      bool success = false;
+      size_t newByteSize = nallocx(newCapacity * sizeof(ElementWrapper), 0);
+
+      // Try to grow in place.
+      //
+      // Note that xallocx(MALLOCX_ZERO) will only zero newly allocated memory,
+      // even if a previous allocation allocated more than we requested.
+      // This is fine; we always use MALLOCX_ZERO with jemalloc and we
+      // always expand our allocation to the real size.
+      if (prevCapacity * sizeof(ElementWrapper) >=
+          jemallocMinInPlaceExpandable) {
+        success = (xallocx(threadEntry->elements, newByteSize, 0, MALLOCX_ZERO)
+                   == newByteSize);
+      }
+
+      // In-place growth failed.
+      if (!success) {
+        success = ((reallocated = static_cast<ElementWrapper*>(
+                    mallocx(newByteSize, MALLOCX_ZERO))) != nullptr);
+      }
+
+      if (success) {
+        // Expand to real size
+        assert(newByteSize / sizeof(ElementWrapper) >= newCapacity);
+        newCapacity = newByteSize / sizeof(ElementWrapper);
+      } else {
+        throw std::bad_alloc();
+      }
+    } else {  // no jemalloc
+      // calloc() is simpler than malloc() followed by memset(), and
+      // potentially faster when dealing with a lot of memory, as it can get
+      // already-zeroed pages from the kernel.
+      reallocated = static_cast<ElementWrapper*>(
+          calloc(newCapacity, sizeof(ElementWrapper)));
+      if (!reallocated) {
+        throw std::bad_alloc();
+      }
+    }
+
+    // Success, update the entry
+    {
+      std::lock_guard<std::mutex> g(meta.lock_);
+
+      if (prevCapacity == 0) {
+        meta.push_back(threadEntry);
+      }
+
+      if (reallocated) {
+       /*
+        * Note: we need to hold the meta lock when copying data out of
+        * the old vector, because some other thread might be
+        * destructing a ThreadLocal and writing to the elements vector
+        * of this thread.
+        */
+        memcpy(reallocated, threadEntry->elements,
+               sizeof(ElementWrapper) * prevCapacity);
+        using std::swap;
+        swap(reallocated, threadEntry->elements);
+      }
+      threadEntry->elementsCapacity = newCapacity;
+    }
+
+    free(reallocated);
+
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+    if (prevCapacity == 0) {
+      pthread_setspecific(meta.pthreadKey_, &meta);
+    }
+#endif
+  }
+
+  static ElementWrapper& get(uint32_t id) {
+    ThreadEntry* threadEntry = getThreadEntry();
+    if (UNLIKELY(threadEntry->elementsCapacity <= id)) {
+      reserve(id);
+      assert(threadEntry->elementsCapacity > id);
+    }
+    return threadEntry->elements[id];
+  }
+};
+
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+template <class Tag>
+FOLLY_TLS ThreadEntry StaticMeta<Tag>::threadEntry_ = {nullptr, 0,
+                                                       nullptr, nullptr};
+#endif
+template <class Tag> StaticMeta<Tag>* StaticMeta<Tag>::inst_ = nullptr;
+
+}  // namespace threadlocal_detail
+}  // namespace folly
+
+#endif /* FOLLY_DETAIL_THREADLOCALDETAIL_H_ */
diff --git a/faux-folly/folly/detail/TurnSequencer.h b/faux-folly/folly/detail/TurnSequencer.h
new file mode 100644
index 0000000..c3dd4a4
--- /dev/null
+++ b/faux-folly/folly/detail/TurnSequencer.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2015 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 <assert.h>
+#include <limits>
+#include <unistd.h>
+
+#include <folly/detail/Futex.h>
+#include <folly/Portability.h>
+
+namespace folly {
+
+namespace detail {
+
+/// A TurnSequencer allows threads to order their execution according to
+/// a monotonically increasing (with wraparound) "turn" value.  The two
+/// operations provided are to wait for turn T, and to move to the next
+/// turn.  Every thread that is waiting for T must have arrived before
+/// that turn is marked completed (for MPMCQueue only one thread waits
+/// for any particular turn, so this is trivially true).
+///
+/// TurnSequencer's state_ holds 26 bits of the current turn (shifted
+/// left by 6), along with a 6 bit saturating value that records the
+/// maximum waiter minus the current turn.  Wraparound of the turn space
+/// is expected and handled.  This allows us to atomically adjust the
+/// number of outstanding waiters when we perform a FUTEX_WAKE operation.
+/// Compare this strategy to sem_t's separate num_waiters field, which
+/// isn't decremented until after the waiting thread gets scheduled,
+/// during which time more enqueues might have occurred and made pointless
+/// FUTEX_WAKE calls.
+///
+/// TurnSequencer uses futex() directly.  It is optimized for the
+/// case that the highest awaited turn is 32 or less higher than the
+/// current turn.  We use the FUTEX_WAIT_BITSET variant, which lets
+/// us embed 32 separate wakeup channels in a single futex.  See
+/// http://locklessinc.com/articles/futex_cheat_sheet for a description.
+///
+/// We only need to keep exact track of the delta between the current
+/// turn and the maximum waiter for the 32 turns that follow the current
+/// one, because waiters at turn t+32 will be awoken at turn t.  At that
+/// point they can then adjust the delta using the higher base.  Since we
+/// need to encode waiter deltas of 0 to 32 inclusive, we use 6 bits.
+/// We actually store waiter deltas up to 63, since that might reduce
+/// the number of CAS operations a tiny bit.
+///
+/// To avoid some futex() calls entirely, TurnSequencer uses an adaptive
+/// spin cutoff before waiting.  The overheads (and convergence rate)
+/// of separately tracking the spin cutoff for each TurnSequencer would
+/// be prohibitive, so the actual storage is passed in as a parameter and
+/// updated atomically.  This also lets the caller use different adaptive
+/// cutoffs for different operations (read versus write, for example).
+/// To avoid contention, the spin cutoff is only updated when requested
+/// by the caller.
+template <template<typename> class Atom>
+struct TurnSequencer {
+  explicit TurnSequencer(const uint32_t firstTurn = 0) noexcept
+      : state_(encode(firstTurn << kTurnShift, 0))
+  {}
+
+  /// Returns true iff a call to waitForTurn(turn, ...) won't block
+  bool isTurn(const uint32_t turn) const noexcept {
+    auto state = state_.load(std::memory_order_acquire);
+    return decodeCurrentSturn(state) == (turn << kTurnShift);
+  }
+
+  /// See tryWaitForTurn
+  /// Requires that `turn` is not a turn in the past.
+  void waitForTurn(const uint32_t turn,
+                Atom<uint32_t>& spinCutoff,
+                const bool updateSpinCutoff) noexcept {
+    bool success = tryWaitForTurn(turn, spinCutoff, updateSpinCutoff);
+    (void) success;
+    assert(success);
+  }
+
+  // Internally we always work with shifted turn values, which makes the
+  // truncation and wraparound work correctly.  This leaves us bits at
+  // the bottom to store the number of waiters.  We call shifted turns
+  // "sturns" inside this class.
+
+  /// Blocks the current thread until turn has arrived.  If
+  /// updateSpinCutoff is true then this will spin for up to kMaxSpins tries
+  /// before blocking and will adjust spinCutoff based on the results,
+  /// otherwise it will spin for at most spinCutoff spins.
+  /// Returns true if the wait succeeded, false if the turn is in the past
+  bool tryWaitForTurn(const uint32_t turn,
+                   Atom<uint32_t>& spinCutoff,
+                   const bool updateSpinCutoff) noexcept {
+    uint32_t prevThresh = spinCutoff.load(std::memory_order_relaxed);
+    const uint32_t effectiveSpinCutoff =
+        updateSpinCutoff || prevThresh == 0 ? kMaxSpins : prevThresh;
+
+    uint32_t tries;
+    const uint32_t sturn = turn << kTurnShift;
+    for (tries = 0; ; ++tries) {
+      uint32_t state = state_.load(std::memory_order_acquire);
+      uint32_t current_sturn = decodeCurrentSturn(state);
+      if (current_sturn == sturn) {
+        break;
+      }
+
+      // wrap-safe version of (current_sturn >= sturn)
+      if(sturn - current_sturn >= std::numeric_limits<uint32_t>::max() / 2) {
+        // turn is in the past
+        return false;
+      }
+
+      // the first effectSpinCutoff tries are spins, after that we will
+      // record ourself as a waiter and block with futexWait
+      if (tries < effectiveSpinCutoff) {
+        asm_volatile_pause();
+        continue;
+      }
+
+      uint32_t current_max_waiter_delta = decodeMaxWaitersDelta(state);
+      uint32_t our_waiter_delta = (sturn - current_sturn) >> kTurnShift;
+      uint32_t new_state;
+      if (our_waiter_delta <= current_max_waiter_delta) {
+        // state already records us as waiters, probably because this
+        // isn't our first time around this loop
+        new_state = state;
+      } else {
+        new_state = encode(current_sturn, our_waiter_delta);
+        if (state != new_state &&
+            !state_.compare_exchange_strong(state, new_state)) {
+          continue;
+        }
+      }
+      state_.futexWait(new_state, futexChannel(turn));
+    }
+
+    if (updateSpinCutoff || prevThresh == 0) {
+      // if we hit kMaxSpins then spinning was pointless, so the right
+      // spinCutoff is kMinSpins
+      uint32_t target;
+      if (tries >= kMaxSpins) {
+        target = kMinSpins;
+      } else {
+        // to account for variations, we allow ourself to spin 2*N when
+        // we think that N is actually required in order to succeed
+        target = std::min<uint32_t>(kMaxSpins,
+                                    std::max<uint32_t>(kMinSpins, tries * 2));
+      }
+
+      if (prevThresh == 0) {
+        // bootstrap
+        spinCutoff.store(target);
+      } else {
+        // try once, keep moving if CAS fails.  Exponential moving average
+        // with alpha of 7/8
+        // Be careful that the quantity we add to prevThresh is signed.
+        spinCutoff.compare_exchange_weak(
+            prevThresh, prevThresh + int(target - prevThresh) / 8);
+      }
+    }
+
+    return true;
+  }
+
+  /// Unblocks a thread running waitForTurn(turn + 1)
+  void completeTurn(const uint32_t turn) noexcept {
+    uint32_t state = state_.load(std::memory_order_acquire);
+    while (true) {
+      assert(state == encode(turn << kTurnShift, decodeMaxWaitersDelta(state)));
+      uint32_t max_waiter_delta = decodeMaxWaitersDelta(state);
+      uint32_t new_state = encode(
+              (turn + 1) << kTurnShift,
+              max_waiter_delta == 0 ? 0 : max_waiter_delta - 1);
+      if (state_.compare_exchange_strong(state, new_state)) {
+        if (max_waiter_delta != 0) {
+          state_.futexWake(std::numeric_limits<int>::max(),
+                           futexChannel(turn + 1));
+        }
+        break;
+      }
+      // failing compare_exchange_strong updates first arg to the value
+      // that caused the failure, so no need to reread state_
+    }
+  }
+
+  /// Returns the least-most significant byte of the current uncompleted
+  /// turn.  The full 32 bit turn cannot be recovered.
+  uint8_t uncompletedTurnLSB() const noexcept {
+    return state_.load(std::memory_order_acquire) >> kTurnShift;
+  }
+
+ private:
+  enum : uint32_t {
+    /// kTurnShift counts the bits that are stolen to record the delta
+    /// between the current turn and the maximum waiter. It needs to be big
+    /// enough to record wait deltas of 0 to 32 inclusive.  Waiters more
+    /// than 32 in the future will be woken up 32*n turns early (since
+    /// their BITSET will hit) and will adjust the waiter count again.
+    /// We go a bit beyond and let the waiter count go up to 63, which
+    /// is free and might save us a few CAS
+    kTurnShift = 6,
+    kWaitersMask = (1 << kTurnShift) - 1,
+
+    /// The minimum spin count that we will adaptively select
+    kMinSpins = 20,
+
+    /// The maximum spin count that we will adaptively select, and the
+    /// spin count that will be used when probing to get a new data point
+    /// for the adaptation
+    kMaxSpins = 2000,
+  };
+
+  /// This holds both the current turn, and the highest waiting turn,
+  /// stored as (current_turn << 6) | min(63, max(waited_turn - current_turn))
+  Futex<Atom> state_;
+
+  /// Returns the bitmask to pass futexWait or futexWake when communicating
+  /// about the specified turn
+  int futexChannel(uint32_t turn) const noexcept {
+    return 1 << (turn & 31);
+  }
+
+  uint32_t decodeCurrentSturn(uint32_t state) const noexcept {
+    return state & ~kWaitersMask;
+  }
+
+  uint32_t decodeMaxWaitersDelta(uint32_t state) const noexcept {
+    return state & kWaitersMask;
+  }
+
+  uint32_t encode(uint32_t currentSturn, uint32_t maxWaiterD) const noexcept {
+    return currentSturn | std::min(uint32_t{ kWaitersMask }, maxWaiterD);
+  }
+};
+
+} // namespace detail
+} // namespace folly
diff --git a/faux-folly/folly/detail/UncaughtExceptionCounter.h b/faux-folly/folly/detail/UncaughtExceptionCounter.h
new file mode 100644
index 0000000..27b3b8f
--- /dev/null
+++ b/faux-folly/folly/detail/UncaughtExceptionCounter.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DETAIL_UNCAUGHTEXCEPTIONCOUNTER_H_
+#define FOLLY_DETAIL_UNCAUGHTEXCEPTIONCOUNTER_H_
+
+#include <exception>
+
+#if defined(__GNUG__) || defined(__CLANG__)
+#define FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS
+namespace __cxxabiv1 {
+// forward declaration (originally defined in unwind-cxx.h from from libstdc++)
+struct __cxa_eh_globals;
+// declared in cxxabi.h from libstdc++-v3
+extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
+}
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400) // MSVC++ 8.0 or greater
+#define FOLLY_EXCEPTION_COUNT_USE_GETPTD
+// forward declaration (originally defined in mtdll.h from MSVCRT)
+struct _tiddata;
+extern "C" _tiddata* _getptd(); // declared in mtdll.h from MSVCRT
+#else
+// Raise an error when trying to use this on unsupported platforms.
+#error "Unsupported platform, don't include this header."
+#endif
+
+
+namespace folly { namespace detail {
+
+/**
+ * Used to check if a new uncaught exception was thrown by monitoring the
+ * number of uncaught exceptions.
+ *
+ * Usage:
+ *  - create a new UncaughtExceptionCounter object
+ *  - call isNewUncaughtException() on the new object to check if a new
+ *    uncaught exception was thrown since the object was created
+ */
+class UncaughtExceptionCounter {
+ public:
+  UncaughtExceptionCounter()
+    : exceptionCount_(getUncaughtExceptionCount()) {}
+
+  UncaughtExceptionCounter(const UncaughtExceptionCounter& other)
+    : exceptionCount_(other.exceptionCount_) {}
+
+  bool isNewUncaughtException() noexcept {
+    return getUncaughtExceptionCount() > exceptionCount_;
+  }
+
+ private:
+  int getUncaughtExceptionCount() noexcept;
+
+  int exceptionCount_;
+};
+
+/**
+ * Returns the number of uncaught exceptions.
+ *
+ * This function is based on Evgeny Panasyuk's implementation from here:
+ * http://fburl.com/15190026
+ */
+inline int UncaughtExceptionCounter::getUncaughtExceptionCount() noexcept {
+#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS)
+  // __cxa_get_globals returns a __cxa_eh_globals* (defined in unwind-cxx.h).
+  // The offset below returns __cxa_eh_globals::uncaughtExceptions.
+  return *(reinterpret_cast<unsigned int*>(static_cast<char*>(
+      static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
+#elif defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
+  // _getptd() returns a _tiddata* (defined in mtdll.h).
+  // The offset below returns _tiddata::_ProcessingThrow.
+  return *(reinterpret_cast<int*>(static_cast<char*>(
+      static_cast<void*>(_getptd())) + sizeof(void*) * 28 + 0x4 * 8));
+#endif
+}
+
+}} // namespaces
+
+#endif /* FOLLY_DETAIL_UNCAUGHTEXCEPTIONCOUNTER_H_ */
diff --git a/faux-folly/folly/docs/Benchmark.md b/faux-folly/folly/docs/Benchmark.md
new file mode 100644
index 0000000..6ac92df
--- /dev/null
+++ b/faux-folly/folly/docs/Benchmark.md
@@ -0,0 +1,295 @@
+`folly/Benchmark.h`
+-----------------
+
+`folly/Benchmark.h` provides a simple framework for writing and
+executing benchmarks. Currently the framework targets only
+single-threaded testing (though you can internally use fork-join
+parallelism and measure total run time).
+
+To use this library, you need to be using gcc 4.6 or later. Include
+`folly/Benchmark.h` and make sure `folly/benchmark.cpp` is part of the
+build (either directly or packaged with a library).
+
+### Overview
+***
+
+Using `folly/Benchmark.h` is very simple. Here's an example:
+
+``` Cpp
+    #include <folly/Benchmark.h>
+    #include <folly/Foreach.h>
+    #include <vector>
+    using namespace std;
+    using namespace folly;
+    BENCHMARK(insertFrontVector) {
+      // Let's insert 100 elements at the front of a vector
+      vector<int> v;
+      FOR_EACH_RANGE (i, 0, 100) {
+        v.insert(v.begin(), i);
+      }
+    }
+    BENCHMARK(insertBackVector) {
+      // Let's insert 100 elements at the back of a vector
+      vector<int> v;
+      FOR_EACH_RANGE (i, 0, 100) {
+        v.insert(v.end(), i);
+      }
+    }
+    int main() {
+      runBenchmarks();
+    }
+```
+
+Compiling and running this code produces to the standard output:
+
+```
+    ===============================================================================
+    test.cpp                                              relative ns/iter  iters/s
+    ===============================================================================
+    insertFrontVector                                                3.84K  260.38K
+    insertBackVector                                                 1.61K  622.75K
+    ===============================================================================
+```
+
+Let's worry about the empty column "relative" later. The table
+contains, for each benchmark, the time spent per call and the converse
+number of calls per second. Numbers are represented in metric notation
+(K for thousands, M for millions etc). As expected, in this example
+the second function is much faster (fewer ns/iter and more iters/s).
+
+The macro `BENCHMARK` introduces a function and also adds it to an
+internal array containing all benchmarks in the system. The defined
+function takes no arguments and returns `void`.
+
+The framework calls the function many times to collect statistics
+about it. Sometimes the function itself would want to do that
+iteration---for example how about inserting `n` elements instead of
+100 elements? To do the iteration internally, use `BENCHMARK` with two
+parameters. The second parameter is the number of iterations and is
+passed by the framework down to the function. The type of the count is
+implicitly `unsigned`. Consider a slightly reworked example:
+
+``` Cpp
+    #include <folly/Benchmark.h>
+    #include <folly/Foreach.h>
+    #include <vector>
+    using namespace std;
+    using namespace folly;
+    BENCHMARK(insertFrontVector, n) {
+      vector<int> v;
+      FOR_EACH_RANGE (i, 0, n) {
+        v.insert(v.begin(), i);
+      }
+    }
+    BENCHMARK(insertBackVector, n) {
+      vector<int> v;
+      FOR_EACH_RANGE (i, 0, n) {
+        v.insert(v.end(), i);
+      }
+    }
+    int main() {
+      runBenchmarks();
+    }
+```
+
+The produced numbers are substantially different:
+
+```
+    ===============================================================================
+    Benchmark                                             relative ns/iter  iters/s
+    ===============================================================================
+    insertFrontVector                                               39.92    25.05M
+    insertBackVector                                                 3.46   288.89M
+    ===============================================================================
+```
+
+Now the numbers indicate the speed of one single insertion because the
+framework assumed the user-defined function used internal iteration
+(which it does). So inserting at the back of a vector is more than 10
+times faster than inserting at the front! Speaking of comparisons...
+
+### Baselines
+***
+
+Choosing one or more good baselines is a crucial activity in any
+measurement. Without a baseline there is little information to derive
+from the sheer numbers. If, for example, you do experimentation with
+algorithms, a good baseline is often an established approach (e.g. the
+built-in `std::sort` for sorting). Essentially all experimental
+numbers should be compared against some baseline.
+
+To support baseline-driven measurements, `folly/Benchmark.h` defines
+`BENCHMARK_RELATIVE`, which works much like `BENCHMARK`, except it
+considers the most recent lexically-ocurring `BENCHMARK` a baseline,
+and fills the "relative" column. Say, for example, we want to use
+front insertion for a vector as a baseline and see how back insertion
+compares with it:
+
+``` Cpp
+    #include <folly/Benchmark.h>
+    #include <folly/Foreach.h>
+    #include <vector>
+    using namespace std;
+    using namespace folly;
+    BENCHMARK(insertFrontVector, n) {
+      vector<int> v;
+      FOR_EACH_RANGE (i, 0, n) {
+        v.insert(v.begin(), i);
+      }
+    }
+    BENCHMARK_RELATIVE(insertBackVector, n) {
+      vector<int> v;
+      FOR_EACH_RANGE (i, 0, n) {
+        v.insert(v.end(), i);
+      }
+    }
+    int main() {
+      runBenchmarks();
+    }
+```
+
+This program prints something like:
+
+```
+    ===============================================================================
+    Benchmark                                             relative ns/iter  iters/s
+    ===============================================================================
+    insertFrontVector                                               42.65    23.45M
+    insertBackVector                                     1208.24%    3.53   283.30M
+    ===============================================================================
+```
+
+showing the 1208.24% relative speed advantage of inserting at the back
+compared to front. The scale is chosen in such a way that 100% means
+identical speed, numbers smaller than 100% indicate the benchmark is
+slower than the baseline, and numbers greater than 100% indicate the
+benchmark is faster. For example, if you see 42% that means the speed
+of the benchmark is 0.42 of the baseline speed. If you see 123%, it
+means the benchmark is 23% or 1.23 times faster.
+
+To close the current benchmark group and start another, simply use
+`BENCHMARK` again.
+
+### Ars Gratia Artis
+***
+
+If you want to draw a horizontal line of dashes (e.g. at the end of a
+group or for whatever reason), use `BENCHMARK_DRAW_LINE()`. The line
+fulfills a purely aesthetic role; it doesn't interact with
+measurements in any way.
+
+``` Cpp
+    BENCHMARK(foo) {
+      Foo foo;
+      foo.doSomething();
+    }
+
+    BENCHMARK_DRAW_LINE();
+
+    BENCHMARK(bar) {
+      Bar bar;
+      bar.doSomething();
+    }
+```
+
+### Suspending a benchmark
+***
+
+Sometimes benchmarking code must do some preparation work that is
+physically inside the benchmark function, but should not take part to
+its time budget. To temporarily suspend the benchmark, use the
+pseudo-statement `BENCHMARK_SUSPEND` as follows:
+
+``` Cpp
+    BENCHMARK(insertBackVector, n) {
+      vector<int> v;
+      BENCHMARK_SUSPEND {
+        v.reserve(n);
+      }
+      FOR_EACH_RANGE (i, 0, n) {
+        v.insert(v.end(), i);
+      }
+    }
+```
+
+The preallocation effected with `v.reserve(n)` will not count toward
+the total run time of the benchmark.
+
+Only the main thread should call `BENCHMARK_SUSPEND` (and of course it
+should not call it while other threads are doing actual work). This is
+because the timer is application-global.
+
+If the scope introduced by `BENCHMARK_SUSPEND` is not desired, you may
+want to "manually" use the `BenchmarkSuspender` type. Constructing
+such an object suspends time measurement, and destroying it resumes
+the measurement. If you want to resume time measurement before the
+destructor, call `dismiss` against the `BenchmarkSuspender`
+object. The previous example could have been written like this:
+
+``` Cpp
+    BENCHMARK(insertBackVector, n) {
+      BenchmarkSuspender braces;
+      vector<int> v;
+      v.reserve(n);
+      braces.dismiss();
+      FOR_EACH_RANGE (i, 0, n) {
+        v.insert(v.end(), i);
+      }
+    }
+```
+
+### `doNotOptimizeAway`
+***
+
+Finally, the small utility function `doNotOptimizeAway` prevents
+compiler optimizations that may interfere with benchmarking . Call
+doNotOptimizeAway(var) against variables that you use for
+benchmarking but otherwise are useless. The compiler tends to do a
+good job at eliminating unused variables, and this function fools it
+into thinking a variable is in fact needed. Example:
+
+``` Cpp
+    BENCHMARK(fpOps, n) {
+      double d = 1;
+      FOR_EACH_RANGE (i, 1, n) {
+        d += i;
+        d -= i;
+        d *= i;
+        d /= i;
+      }
+      doNotOptimizeAway(d);
+    }
+```
+
+### A look under the hood
+***
+
+`folly/Benchmark.h` has a simple, systematic approach to collecting
+timings.
+
+First, it organizes measurements in several large epochs, and takes
+the minimum over all epochs. Taking the minimum gives the closest
+result to the real runtime. Benchmark timings are not a regular random
+variable that fluctuates around an average. Instead, the real time
+we're looking for is one to which there's a variety of additive noise
+(i.e. there is no noise that could actually shorten the benchmark time
+below its real value). In theory, taking an infinite amount of samples
+and keeping the minimum is the actual time that needs
+measuring. That's why the accuracy of benchmarking increases with the
+number of epochs.
+
+Clearly, in real functioning there will also be noise and a variety of
+effects caused by the running context. But the noise during the
+benchmark (straight setup, simple looping) is a poor model for the
+noise in the real application. So taking the minimum across several
+epochs is the most informative result.
+
+Inside each epoch, the function measured is iterated an increasing
+number of times until the total runtime is large enough to make noise
+negligible. At that point the time is collected, and the time per
+iteration is computed. As mentioned, the minimum time per iteration
+over all epochs is the final result.
+
+The timer function used is `clock_gettime` with the `CLOCK_REALTIME`
+clock id. Note that you must use a recent Linux kernel (2.6.38 or
+newer), otherwise the resolution of `CLOCK_REALTIME` is inadequate.
diff --git a/faux-folly/folly/docs/Conv.md b/faux-folly/folly/docs/Conv.md
new file mode 100644
index 0000000..8d285e9
--- /dev/null
+++ b/faux-folly/folly/docs/Conv.md
@@ -0,0 +1,217 @@
+`folly/Conv.h`
+-------------
+
+`folly/Conv.h` is a one-stop-shop for converting values across
+types. Its main features are simplicity of the API (only the
+names `to` and `toAppend` must be memorized), speed
+(folly is significantly faster, sometimes by an order of magnitude,
+than comparable APIs), and correctness.
+
+### Synopsis
+***
+
+All examples below are assume to have included `folly/Conv.h`
+and issued `using namespace folly;` You will need:
+
+``` Cpp
+    // To format as text and append to a string, use toAppend.
+    fbstring str;
+    toAppend(2.5, &str);
+    CHECK_EQ(str, "2.5");
+
+    // Multiple arguments are okay, too. Just put the pointer to string at the end.
+    toAppend(" is ", 2, " point ", 5, &str);
+    CHECK_EQ(str, "2.5 is 2 point 5");
+
+    // You don't need to use fbstring (although it's much faster for conversions and in general).
+    std::string stdStr;
+    toAppend("Pi is about ", 22.0 / 7, &stdStr);
+    // In general, just use to<TargetType>(sourceValue). It returns its result by value.
+    stdStr = to<std::string>("Variadic ", "arguments also accepted.");
+
+    // to<fbstring> is 2.5x faster than to<std::string> for typical workloads.
+    str = to<fbstring>("Variadic ", "arguments also accepted.");
+```
+
+### Integral-to-integral conversion
+***
+
+Using `to<Target>(value)` to convert one integral type to another
+will behave as follows:
+
+* If the target type can accommodate all possible values of the
+  source value, the value is implicitly converted. No further
+  action is taken. Example:
+
+``` Cpp
+        short x;
+        unsigned short y;
+        ...
+        auto a = to<int>(x); // zero overhead conversion
+        auto b = to<int>(y); // zero overhead conversion
+```
+
+* Otherwise, `to` inserts bounds checks and throws
+  `std::range_error` if the target type cannot accommodate the
+  source value. Example:
+
+``` Cpp
+    short x;
+    unsigned short y;
+    long z;
+    ...
+    x = 123;
+    auto a = to<unsigned short>(x); // fine
+    x = -1;
+    a = to<unsigned short>(x); // THROWS
+    z = 2000000000;
+    auto b = to<int>(z); // fine
+    z += 1000000000;
+    b = to<int>(z); // THROWS
+    auto b = to<unsigned int>(z); // fine
+```
+
+### Anything-to-string conversion
+***
+
+As mentioned, there are two primitives for converting anything to
+string: `to` and `toAppend`. They support the same set of source
+types, literally by definition (`to` is implemented in terms of
+`toAppend` for all types). The call `toAppend(value, &str)`
+formats and appends `value` to `str` whereas
+`to<StringType>(value)` formats `value` as a `StringType` and
+returns the result by value. Currently, the supported
+`StringType`s are `std::string` and `fbstring`
+
+Both `toAppend` and `to` with a string type as a target support
+variadic arguments. Each argument is converted in turn. For
+`toAppend` the last argument in a variadic list must be the
+address of a supported string type (no need to specify the string
+type as a template argument).
+
+#### Integral-to-string conversion
+
+Nothing special here - integrals are converted to strings in
+decimal format, with a '-' prefix for negative values. Example:
+
+``` Cpp
+    auto a = to<fbstring>(123);
+    assert(a == "123");
+    a = to<fbstring>(-456);
+    assert(a == "-456");
+```
+
+The conversion implementation is aggressively optimized. It
+converts two digits at a time assisted by fixed-size tables.
+Converting a `long` to an `fbstring` is 3.6x faster than using
+`boost::lexical_cast` and 2.5x faster than using `sprintf` even
+though the latter is used in conjunction with a stack-allocated
+constant-size buffer.
+
+Note that converting integral types to `fbstring` has a
+particular advantage compared to converting to `std::string`
+No integral type (<= 64 bits) has more than 20 decimal digits
+including sign. Since `fbstring` employs the small string
+optimization for up to 23 characters, converting an integral
+to `fbstring` is guaranteed to not allocate memory, resulting
+in significant speed and memory locality gains. Benchmarks
+reveal a 2x gain on a typical workload.
+
+#### `char` to string conversion
+
+Although `char` is technically an integral type, most of the time
+you want the string representation of `'a'` to be `"a"`, not `96`
+That's why `folly/Conv.h` handles `char` as a special case that
+does the expected thing. Note that `signed char` and `unsigned
+char` are still considered integral types.
+
+
+#### Floating point to string conversion
+
+`folly/Conv.h` uses [V8's double conversion](http://code.google.com/p/double-conversion/)
+routines. They are accurate and fast; on typical workloads,
+`to<fbstring>(doubleValue)` is 1.9x faster than `sprintf` and
+5.5x faster than `boost::lexical_cast` (It is also 1.3x faster
+than `to<std::string>(doubleValue)`
+
+#### `const char*` to string conversion
+
+For completeness, `folly/Conv.h` supports `const char*` including
+i.e. string literals. The "conversion" consists, of course, of
+the string itself. Example:
+
+``` Cpp
+    auto s = to<fbstring>("Hello, world");
+    assert(s == "Hello, world");
+```
+
+#### Anything from string conversion (i.e. parsing)
+***
+
+`folly/Conv.h` includes three kinds of parsing routines:
+
+* `to<Type>(const char* begin, const char* end)` rigidly
+  converts the range [begin, end) to `Type` These routines have
+  drastic restrictions (e.g. allow no leading or trailing
+  whitespace) and are intended as an efficient back-end for more
+  tolerant routines.
+* `to<Type>(stringy)` converts `stringy` to `Type` Value
+  `stringy` may be of type `const char*`, `StringPiece`,
+  `std::string`, or `fbstring` (Technically, the requirement is
+  that `stringy` implicitly converts to a `StringPiece`
+* `to<Type>(&stringPiece)` parses with progress information:
+  given `stringPiece` of type `StringPiece` it parses as much
+  as possible from it as type `Type` and alters `stringPiece`
+  to remove the munched characters. This is easiest clarified
+  by an example:
+
+``` Cpp
+    fbstring s = " 1234 angels on a pin";
+    StringPiece pc(s);
+    auto x = to<int>(&pc);
+    assert(x == 1234);
+    assert(pc == " angels on a pin";
+```
+
+Note how the routine ate the leading space but not the trailing one.
+
+#### Parsing integral types
+
+Parsing integral types is unremarkable - decimal format is
+expected, optional `'+'` or `'-'` sign for signed types, but no
+optional `'+'` is allowed for unsigned types. The one remarkable
+element is speed - parsing typical `long` values is 6x faster than
+`sscanf`. `folly/Conv.h` uses aggressive loop unrolling and
+table-assisted SIMD-style code arrangement that avoids integral
+division (slow) and data dependencies across operations
+(ILP-unfriendly). Example:
+
+``` Cpp
+    fbstring str = "  12345  ";
+    assert(to<int>(str) == 12345);
+    str = "  12345six seven eight";
+    StringPiece pc(str);
+    assert(to<int>(&pc) == 12345);
+    assert(str == "six seven eight");
+```
+
+#### Parsing floating-point types
+
+`folly/Conv.h` uses, again, [V8's double-conversion](http://code.google.com/p/double-conversion/)
+routines as back-end. The speed is 3x faster than `sscanf` and
+1.7x faster than in-home routines such as `parse<double>` But
+the more important detail is accuracy - even if you do code a
+routine that works faster than `to<double>` chances are it is
+incorrect and will fail in a variety of corner cases. Using
+`to<double>` is strongly recommended.
+
+Note that if an unparsable string is passed to `to<double>` `NaN`
+is returned, which can be tested for as follows:
+
+``` Cpp
+    fbstring str = "not a double";
+    double d = to<double>(str);
+    if (std::isnan(d)) {
+      // string could not be parsed
+    }
+```
diff --git a/faux-folly/folly/docs/Dynamic.md b/faux-folly/folly/docs/Dynamic.md
new file mode 100644
index 0000000..9de183f
--- /dev/null
+++ b/faux-folly/folly/docs/Dynamic.md
@@ -0,0 +1,174 @@
+`folly/dynamic.h`
+-----------------
+
+`folly/dynamic.h` provides a runtime dynamically typed value for
+C++, similar to the way languages with runtime type systems work
+(e.g. Python). It can hold types from a predetermined set of types
+(ints, bools, arrays of other dynamics, etc), similar to something like
+`boost::variant`, but the syntax is intended to be a little more like
+using the native type directly.
+
+To use `dynamic`, you need to be using gcc 4.6 or later. You'll want to
+include `folly/dynamic.h`.
+
+### Overview
+***
+
+Here are some code samples to get started (assumes a `using
+folly::dynamic;` was used):
+
+``` Cpp
+    dynamic twelve = 12; // creates a dynamic that holds an integer
+    dynamic str = "string"; // yep, this one is an fbstring
+
+    // A few other types.
+    dynamic nul = nullptr;
+    dynamic boolean = false;
+
+    // Arrays can be initialized with brackets.
+    dynamic array = { "array ", "of ", 4, " elements" };
+    assert(array.size() == 4);
+    dynamic emptyArray = {};
+    assert(array.empty());
+
+    // Maps from dynamics to dynamics are called objects.  The
+    // dynamic::object constant is how you make an empty map from dynamics
+    // to dynamics.
+    dynamic map = dynamic::object;
+    map["something"] = 12;
+    map["another_something"] = map["something"] * 2;
+
+    // Dynamic objects may be intialized this way
+    dynamic map2 = dynamic::object("something", 12)("another_something", 24);
+```
+
+### Runtime Type Checking and Conversions
+***
+
+Any operation on a dynamic requires checking at runtime that the
+type is compatible with the operation. If it isn't, you'll get a
+`folly::TypeError`. Other exceptions can also be thrown if
+you try to do something impossible (e.g. if you put a very large
+64-bit integer in and try to read it out as a double).
+
+More examples should hopefully clarify this:
+
+``` Cpp
+    dynamic dint = 42;
+
+    dynamic str = "foo";
+    dynamic anotherStr = str + "something"; // fine
+    dynamic thisThrows = str + dint; // TypeError is raised
+```
+
+Explicit type conversions can be requested for some of the basic types:
+
+``` Cpp
+    dynamic dint = 12345678;
+    dynamic doub = dint.asDouble(); // doub will hold 12345678.0
+    dynamic str = dint.asString(); // str == "12345678"
+
+    dynamic hugeInt = std::numeric_limits<int64_t>::max();
+    dynamic hugeDoub = hugeInt.asDouble();  // throws a folly/Conv.h error,
+                                            // since it can't fit in a double
+```
+
+For more complicated conversions, see [DynamicConverter](DynamicConverter.md).
+
+### Iteration and Lookup
+***
+
+You can iterate over dynamic arrays as you would over any C++ sequence container.
+
+``` Cpp
+    dynamic array = {2, 3, "foo"};
+
+    for (auto& val : array) {
+      doSomethingWith(val);
+    }
+```
+
+You can iterate over dynamic maps by calling `items()`, `keys()`,
+`values()`, which behave similarly to the homonymous methods of Python
+dictionaries.
+
+``` Cpp
+    dynamic obj = dynamic::object(2, 3)("hello", "world")("x", 4);
+
+    for (auto& pair : obj.items()) {
+      // Key is pair.first, value is pair.second
+      processKey(pair.first);
+      processValue(pair.second);
+    }
+
+    for (auto& key : obj.keys()) {
+      processKey(key);
+    }
+
+    for (auto& value : obj.values()) {
+      processValue(value);
+    }
+```
+
+You can find an element by key in a dynamic map using the `find()` method,
+which returns an iterator compatible with `items()`:
+
+``` Cpp
+    dynamic obj = dynamic::object(2, 3)("hello", "world")("x", 4);
+
+    auto pos = obj.find("hello");
+    // pos->first is "hello"
+    // pos->second is "world"
+
+    auto pos = obj.find("no_such_key);
+    // pos == obj.items().end()
+```
+
+### Performance
+***
+
+Dynamic typing is more expensive than static typing, even when
+you do it in C++. ;)
+
+However, some effort has been made to keep `folly::dynamic`
+at least reasonably performant for
+common cases. The heap is only used for arrays and objects, and
+move construction is fully supported. String formatting
+internally also uses the highly performant `folly::to<>` (see
+`folly/Conv.h`).
+
+A trade off to keep in mind though, is that
+`sizeof(folly::dynamic)` is 64 bytes. You probably don't want to
+use it if you need to allocate large numbers of them (prefer
+static types, etc).
+
+### Some Design Rationale
+***
+
+**Q. Why is there no default constructor?**
+
+This is a bit of a limitation of `std::initializer_list<>` for
+this use case. The expression `dynamic d = {}` is required by the
+standard to call the default constructor if one exists (the
+reasoning for this makes sense, since `{}` is part of the concept
+of "uniform initialization", and is intended for use with things
+like `std::vector`). It would be surprising if this expression
+didn't leave `d.isArray()` true, but on the other hand it would
+also be surprising if `dynamic d` left `d.isArray()` as true. The
+solution was just to disallow uninitialized dynamics: every
+dynamic must start out being assigned to some value (or nullptr).
+
+**Q. Why doesn't a dynamic string support begin(), end(), and operator[]?**
+
+The value_type of a dynamic iterator is `dynamic`, and `operator[]`
+(or the `at()` function) has to return a reference to a dynamic.  If
+we wanted this to work for strings, this would mean we'd have to
+support dynamics with a character type, and moreover that the internal
+representation of strings would be such that we can hand out
+references to dynamic as accessors on individual characters.  There
+are a lot of potential efficiency drawbacks with this, and it seems
+like a feature that is not needed too often in practice.
+
+**Q. Isn't this just a poor imitation of the C# language feature?**
+
+Pretty much.
diff --git a/faux-folly/folly/docs/DynamicConverter.md b/faux-folly/folly/docs/DynamicConverter.md
new file mode 100644
index 0000000..4d657f3
--- /dev/null
+++ b/faux-folly/folly/docs/DynamicConverter.md
@@ -0,0 +1,71 @@
+`folly/DynamicConverter.h`
+--------------------------
+
+When dynamic objects contain data of a known type, it is sometimes
+useful to have its well-typed representation. A broad set of
+type-conversions are contained in `DynamicConverter.h`, and
+facilitate the transformation of dynamic objects into their well-typed
+format.
+
+### Usage
+***
+
+Simply pass a dynamic into a templated convertTo:
+
+```
+    dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
+    auto vvi = convertTo<fbvector<fbvector<int>>>(d);
+```
+
+### Supported Types
+***
+
+convertTo naturally supports conversions to
+
+1. arithmetic types (such as int64_t, unsigned short, bool, and double)
+2. fbstring, std::string
+3. containers and map-containers
+
+NOTE:
+
+convertTo<Type> will assume that Type is a container if
+* it has a Type::value_type, and
+* it has a Type::iterator, and
+* it has a constructor that accepts two InputIterators
+
+Additionally, convertTo<Type> will assume that Type is a map if
+* it has a Type::key_type, and
+* it has a Type::mapped_type, and
+* value_type is a pair of const key_type and mapped_type
+
+If Type meets the container criteria, then it will be constructed
+by calling its InputIterator constructor.
+
+### Customization
+***
+
+If you want to use convertTo to convert dynamics into your own custom
+class, then all you have to do is provide a template specialization
+of DynamicConverter with the static method convert. Make sure you put it
+in namespace folly.
+
+Example:
+
+``` Cpp
+    struct Token {
+      int kind_;
+      fbstring lexeme_;
+      
+      explicit Token(int kind, const fbstring& lexeme)
+        : kind_(kind), lexeme_(lexeme) {}
+    };
+    namespace folly {
+    template <> struct DynamicConverter<Token> {
+      static Token convert(const dynamic& d) {
+        int k = convertTo<int>(d["KIND"]);
+        fbstring lex = convertTo<fbstring>(d["LEXEME"]);
+        return Token(k, lex);
+      }
+    };
+    }
+```
diff --git a/faux-folly/folly/docs/FBString.md b/faux-folly/folly/docs/FBString.md
new file mode 100644
index 0000000..bfd69f1
--- /dev/null
+++ b/faux-folly/folly/docs/FBString.md
@@ -0,0 +1,46 @@
+`folly/FBString.h`
+------------------
+
+`fbstring` is a drop-in replacement for `std::string`. The main
+benefit of `fbstring` is significantly increased performance on
+virtually all important primitives. This is achieved by using a
+three-tiered storage strategy and by cooperating with the memory
+allocator. In particular, `fbstring` is designed to detect use of
+jemalloc and cooperate with it to achieve significant improvements in
+speed and memory usage.
+
+`fbstring` supports x32 and x64 architectures. Porting it to big endian
+architectures would require some changes.
+
+### Storage strategies
+***
+
+* Small strings (<= 23 chars) are stored in-situ without memory
+  allocation.
+
+* Medium strings (24 - 255 chars) are stored in malloc-allocated
+  memory and copied eagerly.
+
+* Large strings (> 255 chars) are stored in malloc-allocated memory and
+  copied lazily.
+
+### Implementation highlights
+***
+
+* 100% compatible with `std::string`.
+
+* Thread-safe reference counted copy-on-write for strings "large"
+  strings (> 255 chars).
+
+* Uses `malloc` instead of allocators.
+
+* Jemalloc-friendly. `fbstring` automatically detects if application
+  uses jemalloc and if so, significantly improves allocation
+  strategy by using non-standard jemalloc extensions.
+
+* `find()` is implemented using simplified Boyer-Moore
+  algorithm. Casual tests indicate a 30x speed improvement over
+  `string::find()` for successful searches and a 1.5x speed
+  improvement for failed searches.
+
+* Offers conversions to and from `std::string`.
\ No newline at end of file
diff --git a/faux-folly/folly/docs/FBVector.md b/faux-folly/folly/docs/FBVector.md
new file mode 100644
index 0000000..21edbe8
--- /dev/null
+++ b/faux-folly/folly/docs/FBVector.md
@@ -0,0 +1,241 @@
+`folly/FBvector.h`
+------------------
+
+Simply replacing `std::vector` with `folly::fbvector` (after
+having included the `folly/FBVector.h` header file) will
+improve the performance of your C++ code using vectors with
+common coding patterns. The improvements are always non-negative,
+almost always measurable, frequently significant, sometimes
+dramatic, and occasionally spectacular.
+
+### Sample
+***
+
+    folly::fbvector<int> numbers({0, 1, 2, 3});
+    numbers.reserve(10);
+    for (int i = 4; i < 10; i++) {
+      numbers.push_back(i * 2);
+    }
+    assert(numbers[6] == 12);
+
+### Motivation
+***
+
+`std::vector` is the stalwart abstraction many use for
+dynamically-allocated arrays in C++. It is also the best known
+and most used of all containers. It may therefore seem a
+surprise that `std::vector` leaves important - and sometimes
+vital - efficiency opportunities on the table. This document
+explains how our own drop-in abstraction `fbvector` improves key
+performance aspects of `std::vector`. Refer to
+folly/test/FBVectorTest.cpp for a few benchmarks.
+
+### Memory Handling
+***
+
+It is well known that `std::vector` grows exponentially (at a
+constant factor) in order to avoid quadratic growth performance.
+The trick is choosing a good factor (any factor greater than 1
+ensures O(1) amortized append complexity towards infinity). A
+factor that's too small causes frequent vector reallocation; one
+that's too large forces the vector to consume much more memory
+than needed. The initial HP implementation by Stepanov used a
+growth factor of 2, i.e. whenever you'd `push_back` into a vector
+without there being room, it would double the current capacity.
+
+With time, other compilers reduced the growth factor to 1.5, but
+gcc has staunchly used a growth factor of 2. In fact it can be
+mathematically proven that a growth factor of 2 is rigorously the
+<i>worst</i> possible because it never allows the vector to reuse
+any of its previously-allocated memory. That makes the vector cache-
+unfriendly and memory manager unfriendly.
+
+To see why that's the case, consider a large vector of capacity C
+residing somewhere at the beginning of an initially unoccupied
+chunk. When the request for growth comes about, the vector
+(assuming no in-place resizing, see the appropriate section in
+this document) will allocate a chunk next to its current chunk,
+copy its existing data, and then deallocate the old chunk. So now
+we have a chunk of size C followed by a chunk of size k * C.
+Continuing this process we'll then have a chunk of size k * k * C
+to the right and so on. That leads to a series of the form (using
+^^ for power):
+
+    C, C*k,  C*k^^2, C*k^^3, ...
+
+If we choose k = 2 we know that every element in the series will
+be strictly larger than the sum of all previous ones because of
+the remarkable equality:
+
+    1 + 2^^1 + 2^^2 + 2^^3... + 2^^n = 2^^(n+1) - 1
+
+What that really means is that the new request for a chunk will
+be never satisfiable by coalescing all previously-used chunks.
+This is not quite what you'd want.
+
+We would of course want the vector to not crawl forward in
+memory, but instead to move back to its previously-allocated
+chunks. Any number smaller than 2 guarantees that you'll be able
+at some point to reuse the previous chunks. Going through the
+math reveals the equation:
+
+    k^^n <= 1 + k + k^^2 + ... + k^^(n-2)
+
+If some number n satisfies that equation, it means you can reuse
+memory after n reallocations. The graphical solver below reveals
+that choosing k = 1.5 (blue line) allows memory reuse after 4
+reallocations, choosing k = 1.45 (red line) allows memory reuse
+after 3 reallocations, and choosing k = 1.3 (black line) allows
+reuse after only 2 reallocations.
+
+![graphical solutions](./Fbvector--graphical_solutions.png)
+
+Of course, the above makes a number of simplifying assumptions
+about how the memory allocator works, but definitely you don't
+want to choose the theoretically absolute worst growth factor.
+`fbvector` uses a growth factor of 1.5. That does not impede good
+performance at small sizes because of the way `fbvector`
+cooperates with jemalloc (below).
+
+### The jemalloc Connection
+***
+
+Virtually all modern allocators allocate memory in fixed-size
+quanta that are chosen to minimize management overhead while at
+the same time offering good coverage at low slack. For example, an
+allocator may choose blocks of doubling size (32, 64, 128,
+<t_co>, ...) up to 4096, and then blocks of size multiples of a
+page up until 1MB, and then 512KB increments and so on.
+
+As discussed above, `std::vector` also needs to (re)allocate in
+quanta. The next quantum is usually defined in terms of the
+current size times the infamous growth constant. Because of this
+setup, `std::vector` has some slack memory at the end much like
+an allocated block has some slack memory at the end.
+
+It doesn't take a rocket surgeon to figure out that an allocator-
+aware `std::vector` would be a marriage made in heaven: the
+vector could directly request blocks of "perfect" size from the
+allocator so there would be virtually no slack in the allocator.
+Also, the entire growth strategy could be adjusted to work
+perfectly with allocator's own block growth strategy. That's
+exactly what `fbvector` does - it automatically detects the use
+of jemalloc and adjusts its reallocation strategy accordingly.
+
+But wait, there's more. Many memory allocators do not support in-
+place reallocation, although most of them could. This comes from
+the now notorious design of `realloc()` to opaquely perform
+either in-place reallocation or an allocate-memcpy-deallocate
+cycle. Such lack of control subsequently forced all clib-based
+allocator designs to avoid in-place reallocation, and that
+includes C++'s `new` and `std::allocator`. This is a major loss of
+efficiency because an in-place reallocation, being very cheap,
+may mean a much less aggressive growth strategy. In turn that
+means less slack memory and faster reallocations.
+
+### Object Relocation
+***
+
+One particularly sensitive topic about handling C++ values is
+that they are all conservatively considered <i>non-
+relocatable</i>. In contrast, a relocatable value would preserve
+its invariant even if its bits were moved arbitrarily in memory.
+For example, an `int32` is relocatable because moving its 4 bytes
+would preserve its actual value, so the address of that value
+does not "matter" to its integrity.
+
+C++'s assumption of non-relocatable values hurts everybody for
+the benefit of a few questionable designs. The issue is that
+moving a C++ object "by the book" entails (a) creating a new copy
+from the existing value; (b) destroying the old value. This is
+quite vexing and violates common sense; consider this
+hypothetical conversation between Captain Picard and an
+incredulous alien:
+
+Incredulous Alien: "So, this teleporter, how does it work?"<br>
+Picard: "It beams people and arbitrary matter from one place to
+another."<br> Incredulous Alien: "Hmmm... is it safe?"<br>
+Picard: "Yes, but earlier models were a hassle. They'd clone the
+person to another location. Then the teleporting chief would have
+to shoot the original. Ask O'Brien, he was an intern during those
+times. A bloody mess, that's what it was."
+
+Only a tiny minority of objects are genuinely non-relocatable:
+
+* Objects that use internal pointers, e.g.:
+
+    class Ew {
+      char buffer[1024];
+      char * pointerInsideBuffer;
+    public:
+      Ew() : pointerInsideBuffer(buffer) {}
+      ...
+    }
+
+* Objects that need to update "observers" that store pointers to them.
+
+The first class of designs can always be redone at small or no
+cost in efficiency. The second class of objects should not be
+values in the first place - they should be allocated with `new`
+and manipulated using (smart) pointers. It is highly unusual for
+a value to have observers that alias pointers to it.
+
+Relocatable objects are of high interest to `std::vector` because
+such knowledge makes insertion into the vector and vector
+reallocation considerably faster: instead of going to Picard's
+copy-destroy cycle, relocatable objects can be moved around
+simply by using `memcpy` or `memmove`. This optimization can
+yield arbitrarily high wins in efficiency; for example, it
+transforms `vector< vector<double> >` or `vector< hash_map<int,
+string> >` from risky liabilities into highly workable
+compositions.
+
+In order to allow fast relocation without risk, `fbvector` uses a
+trait `folly::IsRelocatable` defined in `"folly/Traits.h"`. By default,
+`folly::IsRelocatable::value` conservatively yields false. If
+you know that your type `Widget` is in fact relocatable, go right
+after `Widget`'s definition and write this:
+
+    // at global namespace level
+    namespace folly {
+      struct IsRelocatable<Widget> : boost::true_type {};
+    }
+
+If you don't do this, `fbvector<Widget>` will fail to compile
+with a `BOOST_STATIC_ASSERT`.
+
+#### Additional Constraints
+
+Similar improvements are possible in presence of a "simple" type
+- more specifically, one that has a trivial assignment (i.e.
+assignment is the same as bitblitting the bits over) or a nothrow
+default constructor. These traits are used gainfully by
+`fbvector` in a variety of places. Fortunately, these traits are
+already present in the C++ standard (well, currently in Boost).
+To summarize, in order to work with `fbvector`, a type `Widget`
+must pass:
+
+    BOOST_STATIC_ASSERT(
+      IsRelocatable<Widget>::value &&
+      (boost::has_trivial_assign<T>::value || boost::has_nothrow_constructor<T>::value));
+
+These traits go hand in hand; for example, it would be very
+difficult to design a class that satisfies one branch of the
+conjunction above but not the other. `fbvector` uses these simple
+constraints to minimize the number of copies made on many common
+operations such as `push_back`, `insert`, or `resize`.
+
+To make it easy for you to state assumptions about a given type
+or family of parameterized types, check Traits.h and in
+particular handy family of macros FOLLY_ASSUME_FBVECTOR_COMPATIBLE*.
+
+### Miscellaneous
+***
+
+`fbvector` uses a careful implementation all around to make
+sure it doesn't lose efficiency through the cracks. Some future
+directions may be in improving raw memory copying (`memcpy` is
+not an intrinsic in gcc and does not work terribly well for
+large chunks) and in furthering the collaboration with
+jemalloc. Have fun!
+
diff --git a/faux-folly/folly/docs/Fbvector--graphical_solutions.png b/faux-folly/folly/docs/Fbvector--graphical_solutions.png
new file mode 100644
index 0000000..c4ac685
--- /dev/null
+++ b/faux-folly/folly/docs/Fbvector--graphical_solutions.png
Binary files differ
diff --git a/faux-folly/folly/docs/Format.md b/faux-folly/folly/docs/Format.md
new file mode 100644
index 0000000..b822c21
--- /dev/null
+++ b/faux-folly/folly/docs/Format.md
@@ -0,0 +1,206 @@
+`folly/Format.h`
+----------------
+
+`folly/Format.h` provides a fast, powerful, type-safe, flexible facility
+for formatting text, using a specification language similar to Python's
+[str.format](http://docs.python.org/library/string.html#formatstrings).
+By default, it can format strings, numbers (integral and floating point),
+and dynamically-typed `folly::dynamic` objects, and can extract values from
+random-access containers and string-keyed maps.  In many cases, `format` is
+faster than `sprintf` as well as being fully type-safe.
+
+To use `format`, you need to be using gcc 4.6 or later.  You'll want to include
+`folly/Format.h`.
+
+### Overview
+***
+
+Here are some code samples to get started:
+
+``` Cpp
+using folly::format;
+using folly::sformat;
+using folly::vformat;
+using folly::svformat;
+
+// Objects produced by format() can be streamed without creating
+// an intermediary string; {} yields the next argument using default
+// formatting.
+std::cout << format("The answers are {} and {}", 23, 42);
+// => "The answers are 23 and 42"
+
+// If you just want the string, though, you're covered.
+std::string result = sformat("The answers are {} and {}", 23, 42);
+// => "The answers are 23 and 42"
+
+// To insert a literal '{' or '}', just double it.
+std::cout << format("{} {{}} {{{}}}", 23, 42);
+// => "23 {} {42}"
+
+// Arguments can be referenced out of order, even multiple times
+std::cout << format("The answers are {1}, {0}, and {1} again", 23, 42);
+// => "The answers are 42, 23, and 42 again"
+
+// It's perfectly fine to not reference all arguments
+std::cout << format("The only answer is {1}", 23, 42);
+// => "The only answer is 42"
+
+// Values can be extracted from indexable containers
+// (random-access sequences and integral-keyed maps), and also from
+// string-keyed maps
+std::vector<int> v {23, 42};
+std::map<std::string, std::string> m { {"what", "answer"} };
+std::cout << format("The only {1[what]} is {0[1]}", v, m);
+// => "The only answer is 42"
+
+// If you only have one container argument, vformat makes the syntax simpler
+std::map<std::string, std::string> m { {"what", "answer"}, {"value", "42"} };
+std::cout << vformat("The only {what} is {value}", m);
+// => "The only answer is 42"
+// same as
+std::cout << format("The only {0[what]} is {0[value]}", m);
+// => "The only answer is 42"
+// And if you just want the string,
+std::string result = svformat("The only {what} is {value}", m);
+// => "The only answer is 42"
+std::string result = sformat("The only {0[what]} is {0[value]}", m);
+// => "The only answer is 42"
+
+// {} works for vformat too
+std::vector<int> v {42, 23};
+std::cout << vformat("{} {}", v);
+// => "42 23"
+
+// format and vformat work with pairs and tuples
+std::tuple<int, std::string, int> t {42, "hello", 23};
+std::cout << vformat("{0} {2} {1}", t);
+// => "42 23 hello"
+
+// Format supports width, alignment, arbitrary fill, and various
+// format specifiers, with meanings similar to printf
+// "X<10": fill with 'X', left-align ('<'), width 10
+std::cout << format("{:X<10} {}", "hello", "world");
+// => "helloXXXXX world"
+
+// Field width may be a runtime value rather than part of the format string
+int x = 6;
+std::cout << format("{:-^*}", x, "hi");
+// => "--hi--"
+
+// Explicit arguments work with dynamic field width, as long as indexes are
+// given for both the value and the field width.
+std::cout << format("{2:+^*0}",
+9, "unused", 456); // => "+++456+++"
+
+// Format supports printf-style format specifiers
+std::cout << format("{0:05d} decimal = {0:04x} hex", 42);
+// => "00042 decimal = 002a hex"
+
+// Formatter objects may be written to a string using folly::to or
+// folly::toAppend (see folly/Conv.h), or by calling their appendTo(),
+// str(), and fbstr() methods
+std::string s = format("The only answer is {}", 42).str();
+std::cout << s;
+// => "The only answer is 42"
+```
+
+
+### Format string syntax
+***
+
+Format string (`format`):
+`"{" [arg_index] ["[" key "]"] [":" format_spec] "}"`
+
+- `arg_index`: index of argument to format; default = next argument.  Note
+  that a format string may have either default argument indexes or
+  non-default argument indexes, but not both (to avoid confusion).
+- `key`: if the argument is a container (C-style array or pointer,
+  `std::array`, vector, deque, map), you may use this
+  to select the element to format; works with random-access sequences and
+  integer- and string-keyed maps.  Multiple level keys work as well, with
+  components separated with "."; for example, given
+  `map<string, map<string, string>> m`, `{[foo.bar]}` selects
+  `m["foo"]["bar"]`.
+- `format_spec`: format specification, see below
+
+Format string (`vformat`):
+`"{" [ key ] [":" format_spec] "}"`
+
+- `key`: select the argument to format from the container argument;
+  works with random-access sequences and integer- and string-keyed maps.
+  Multiple level keys work as well, with components separated with "."; for
+  example, given `map<string, map<string, string>> m`, `{foo.bar}` selects
+  `m["foo"]["bar"]`.
+- `format_spec`: format specification, see below
+
+Format specification:
+`[[fill] align] [sign] ["#"] ["0"] [width] [","] ["." precision] ["."] [type]`
+
+- `fill` (may only be specified if `align` is also specified): pad with this
+  character ('` `' (space) or '`0`' (zero) might be useful; space is default)
+- `align`: one of '`<`', '`>`', '`=`', '`^`':
+    - '`<`': left-align (default for most objects)
+    - '`>`': right-align (default for numbers)
+    - '`=`': pad after sign, but before significant digits; used to print
+            `-0000120`; only valid for numbers
+    - '`^`': center
+- `sign`: one of '`+`', '`-`', ' ' (space) (only valid for numbers)
+    - '`+`': output '`+`' if positive or zero, '`-`' if negative
+    - '`-`': output '`-`' if negative, nothing otherwise (default)
+    - '` `' (space): output '` `' (space) if positive or zero, '`-`' if negative
+- '`#`': output base prefix (`0` for octal, `0b` or `0B` for binary, `0x` or
+  `0X` for hexadecimal; only valid for integers)
+- '`0`': 0-pad after sign, same as specifying "`0=`" as the `fill` and
+  `align` parameters (only valid for numbers)
+- `width`: minimum field width. May be '`*`' to indicate that the field width
+  is given by an argument. Defaults to the next argument (preceding the value
+  to be formatted) but an explicit argument index may be given following the
+  '`*`'. Not supported in `vformat()`.
+- '`,`' (comma): output comma as thousands' separator (only valid for integers,
+  and only for decimal output)
+- `precision` (not allowed for integers):
+    - for floating point values, number of digits after decimal point ('`f`' or
+      '`F`' presentation) or number of significant digits ('`g`' or '`G`')
+    - for others, maximum field size (truncate subsequent characters)
+- '`.`' (when used after precision or in lieu of precison): Forces a trailing
+  decimal point to make it clear this is a floating point value.
+- `type`: presentation format, see below
+
+Presentation formats:
+
+- Strings (`folly::StringPiece`, `std::string`, `folly::fbstring`,
+  `const char*`):
+    - '`s`' (default)
+- Integers:
+    - '`b`': output in binary (base 2) ("`0b`" prefix if '`#`' specified)
+    - '`B`': output in binary (base 2) ("`0B`" prefix if '`#`' specified)
+    - '`c`': output as a character (cast to `char`)
+    - '`d`': output in decimal (base 10) (default)
+    - '`o`': output in octal (base 8)
+    - '`O`': output in octal (base 8) (same as '`o`')
+    - '`x`': output in hexadecimal (base 16) (lower-case digits above 9)
+    - '`X`': output in hexadecimal (base 16) (upper-case digits above 9)
+    - '`n`': locale-aware output (currently same as '`d`')
+- `bool`:
+    - default: output "`true`" or "`false`" as strings
+    - integer presentations allowed as well
+- `char`:
+    - same as other integers, but default is '`c`' instead of '`d`'
+- Floating point (`float`, `double`; `long double` is not implemented):
+    - '`e`': scientific notation using '`e`' as exponent character
+    - '`E`': scientific notation using '`E`' as exponent character
+    - '`f`': fixed point
+    - '`F`': fixed point (same as '`f`')
+    - '`g`': general; use either '`f`' or '`e`' depending on magnitude (default)
+    - '`G`': general; use either '`f`' or '`E`' depending on magnitude
+    - '`n`': locale-aware version of '`g`' (currently same as '`g`')
+    - '`%`': percentage: multiply by 100 then display as '`f`'
+
+
+### Extension
+***
+
+You can extend `format` for your own class by providing a specialization for
+`folly::FormatValue`.  See `folly/Format.h` and `folly/FormatArg.h` for
+details, and the existing specialization for `folly::dynamic` in
+`folly/dynamic-inl.h` for an implementation example.
diff --git a/faux-folly/folly/docs/Histogram.md b/faux-folly/folly/docs/Histogram.md
new file mode 100644
index 0000000..03bc3e8
--- /dev/null
+++ b/faux-folly/folly/docs/Histogram.md
@@ -0,0 +1,104 @@
+`folly/Histogram.h`
+-------------------
+
+### Classes
+***
+
+#### `Histogram`
+
+`Histogram.h` defines a simple histogram class, templated on the type of data
+you want to store.  This class is useful for tracking a large stream of data
+points, where you want to remember the overall distribution of the data, but do
+not need to remember each data point individually.
+
+Each histogram bucket stores the number of data points that fell in the bucket,
+as well as the overall sum of the data points in the bucket.  Note that no
+overflow checking is performed, so if you have a bucket with a large number of
+very large values, it may overflow and cause inaccurate data for this bucket.
+As such, the histogram class is not well suited to storing data points with
+very large values.  However, it works very well for smaller data points such as
+request latencies, request or response sizes, etc.
+
+In addition to providing access to the raw bucket data, the `Histogram` class
+also provides methods for estimating percentile values.  This allows you to
+estimate the median value (the 50th percentile) and other values such as the
+95th or 99th percentiles.
+
+All of the buckets have the same width.  The number of buckets and bucket width
+is fixed for the lifetime of the histogram.  As such, you do need to know your
+expected data range ahead of time in order to have accurate statistics.  The
+histogram does keep one bucket to store all data points that fall below the
+histogram minimum, and one bucket for the data points above the maximum.
+However, because these buckets don't have a good lower/upper bound, percentile
+estimates in these buckets may be inaccurate.
+
+#### `HistogramBuckets`
+
+The `Histogram` class is built on top of `HistogramBuckets`.
+`HistogramBuckets` provides an API very similar to `Histogram`, but allows a
+user-defined bucket class.  This allows users to implement more complex
+histogram types that store more than just the count and sum in each bucket.
+
+When computing percentile estimates `HistogramBuckets` allows user-defined
+functions for computing the average value and data count in each bucket.  This
+allows you to define more complex buckets which may have multiple different
+ways of computing the average value and the count.
+
+For example, one use case could be tracking timeseries data in each bucket.
+Each set of timeseries data can have independent data in the bucket, which can
+show how the data distribution is changing over time.
+
+### Example Usage
+***
+
+Say we have code that sends many requests to remote services, and want to
+generate a histogram showing how long the requests take.  The following code
+will initialize histogram with 50 buckets, tracking values between 0 and 5000.
+(There are 50 buckets since the bucket width is specified as 100.  If the
+bucket width is not an even multiple of the histogram range, the last bucket
+will simply be shorter than the others.)
+
+``` Cpp
+    folly::Histogram<int64_t> latencies(100, 0, 5000);
+```
+
+The addValue() method is used to add values to the histogram.  Each time a
+request finishes we can add its latency to the histogram:
+
+``` Cpp
+    latencies.addValue(now - startTime);
+```
+
+You can access each of the histogram buckets to display the overall
+distribution.  Note that bucket 0 tracks all data points that were below the
+specified histogram minimum, and the last bucket tracks the data points that
+were above the maximum.
+
+``` Cpp
+    unsigned int numBuckets = latencies.getNumBuckets();
+    cout << "Below min: " << latencies.getBucketByIndex(0).count << "\n";
+    for (unsigned int n = 1; n < numBuckets - 1; ++n) {
+      cout << latencies.getBucketMin(n) << "-" << latencies.getBucketMax(n)
+           << ": " << latencies.getBucketByIndex(n).count << "\n";
+    }
+    cout << "Above max: "
+         << latencies.getBucketByIndex(numBuckets - 1).count << "\n";
+```
+
+You can also use the `getPercentileEstimate()` method to estimate the value at
+the Nth percentile in the distribution.  For example, to estimate the median,
+as well as the 95th and 99th percentile values:
+
+``` Cpp
+    int64_t median = latencies.getPercentileEstimate(0.5);
+    int64_t p95 = latencies.getPercentileEstimate(0.95);
+    int64_t p99 = latencies.getPercentileEstimate(0.99);
+```
+
+### Thread Safety
+***
+
+Note that `Histogram` and `HistogramBuckets` objects are not thread-safe.  If
+you wish to access a single `Histogram` from multiple threads, you must perform
+your own locking to ensure that multiple threads do not access it at the same
+time.
diff --git a/faux-folly/folly/docs/Makefile b/faux-folly/folly/docs/Makefile
new file mode 100644
index 0000000..97be3da
--- /dev/null
+++ b/faux-folly/folly/docs/Makefile
@@ -0,0 +1,33 @@
+SOURCES=$(wildcard *.md)
+PDF=$(SOURCES:%.md=%.pdf)
+HTML=$(SOURCES:%.md=%.html)
+INSTALL=install -c -m 644
+PYTHON=python
+PANDOCARGS=-s
+PANDOC=/usr/bin/pandoc
+
+export LANGUAGE=C
+export LC_ALL=C
+
+all: html index.html
+
+pdf: $(PDF)
+
+html: $(HTML)
+
+# This needs pandoc 1.9 or later to work
+%.pdf: %.md
+	$(PANDOC) -f markdown -o $*.pdf $*.md
+
+%.html: %.md style.css
+	$(PANDOC) $(PANDOCARGS) -H style.css -f markdown -t html --toc -o $*.html $*.md
+
+docs.md: $(SOURCES) style.css
+	$(PANDOC) $(PANDOCARGS) -H style.css -f markdown -t markdown --toc -o $@ *.md
+
+index.html: $(SOURCES) style.css
+	$(PANDOC) $(PANDOCARGS) -H style.css -f markdown -t html --toc -o $@ *.md
+
+
+clean:
+	$(RM) $(PDF) $(HTML) index.html
diff --git a/faux-folly/folly/docs/Overview.md b/faux-folly/folly/docs/Overview.md
new file mode 100644
index 0000000..fc5cf02
--- /dev/null
+++ b/faux-folly/folly/docs/Overview.md
@@ -0,0 +1,182 @@
+`folly/`
+------
+
+### Introduction
+
+Folly (acronymed loosely after Facebook Open Source Library) is a
+library of C++11 components designed with practicality and efficiency
+in mind. It complements (as opposed to competing against) offerings
+such as Boost and of course `std`. In fact, we embark on defining our
+own component only when something we need is either not available, or
+does not meet the needed performance profile.
+
+Performance concerns permeate much of Folly, sometimes leading to
+designs that are more idiosyncratic than they would otherwise be.
+Good performance at large scale is a unifying theme in all of Folly.
+
+### Logical Design
+
+Folly is a collection of relatively independent components, some as
+simple as a few symbols. There is no restriction on internal
+dependencies, meaning that a given folly module may use any other
+folly components.
+
+All symbols are defined in the top-level namespace `folly`, except of
+course macros. Macro names are ALL_UPPERCASE. Namespace `folly`
+defines other internal namespaces such as `internal` or `detail`. User
+code should not depend on symbols in those namespaces.
+
+### Physical Design
+
+At the top level Folly uses the classic "stuttering" scheme
+`folly/folly` used by Boost and others. The first directory serves as
+an installation root of the library (with possible versioning a la
+`folly-1.0/`), and the second is to distinguish the library when
+including files, e.g. `#include <folly/FBString.h>`.
+
+The directory structure is flat (mimicking the namespace structure),
+i.e. we don't have an elaborate directory hierarchy (it is possible
+this will change in future versions).
+
+The `folly/folly/test` subdirectory includes the unittests for all
+components, usually named `ComponentXyzTest.cpp` for each
+`ComponentXyz.*`. The `folly/folly/docs` directory contains
+documentation.
+
+### Compatibility
+
+Currently, `folly` has been tested on gcc 4.6 on 64-bit installations
+of Fedora 17, Ubuntu 12.04, and Debian wheezy. It might work unmodified
+on other 64-bit Linux platforms.
+
+### Components
+
+Below is a list of Folly components in alphabetical order, along with
+a brief description of each.
+
+#### [`Benchmark.h`](Benchmark.md)
+
+A small framework for benchmarking code. Client code registers
+benchmarks, optionally with an argument that dictates the scale of the
+benchmark (iterations, working set size etc). The framework runs
+benchmarks (subject to a command-line flag) and produces formatted
+output with timing information.
+
+#### `Bits.h`
+
+Various bit manipulation utilities optimized for speed; includes functions
+that wrap the
+[ffsl(l)](http://linux.die.net/man/3/ffsll) primitives in a uniform
+interface.
+
+#### [`Conv.h`](Conv.md)
+
+A variety of data conversion routines (notably to and from string),
+optimized for speed and safety.
+
+#### [`dynamic.h`](Dynamic.md)
+
+Dynamically-typed object, created with JSON objects in mind.
+
+#### `Endian.h`
+
+Endian conversion primitives.
+
+####`Escape.h`
+
+Escapes a string in C style.
+
+####`eventfd.h`
+
+Wrapper around the
+[`eventfd`](http://www.kernel.org/doc/man-pages/online/pages/man2/eventfd.2.html)
+system call.
+
+####[`FBString.h`](FBString.md)
+
+A drop-in implementation of `std::string` with a variety of optimizations.
+
+####[`FBVector.h`](FBVector.md)
+
+A mostly drop-in implementation of `std::vector` with a variety of
+optimizations.
+
+####`Foreach.h`
+
+Pseudo-statements (implemented as macros) for iteration.
+
+####[`Format.h`](Format.md)
+
+Python-style formatting utilities.
+
+####`Hash.h`
+
+Various popular hash function implementations.
+
+####[`Histogram.h`](Histogram.md)
+
+A simple class for collecting histogram data.
+
+####`Likely.h`
+
+Wrappers around [`__builtin_expect`](http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html).
+
+####`Malloc.h`
+
+Memory allocation helpers, particularly when using jemalloc.
+
+####`MapUtil.h`
+
+Helpers for finding items in associative containers (such as
+`std::map` and `std::unordered_map`).
+
+####`Preprocessor.h`
+
+Necessarily evil stuff.
+
+####`PrettyPrint.h`
+
+Pretty-printer for numbers that appends suffixes of unit used: bytes
+(kb, MB, ...), metric suffixes (k, M, G, ...), and time (s, ms, us,
+ns, ...).
+
+####`Random.h`
+
+Defines only one function---`randomNumberSeed()`.
+
+####`Range.h`
+
+Boost-style range facility and the `StringPiece` specialization.
+
+####`ScopeGuard.h`
+
+C++11 incarnation of the old [ScopeGuard](http://drdobbs.com/184403758) idiom.
+
+####[`SmallLocks.h`](SmallLocks.md)
+
+Very small spin locks (1 byte and 1 bit).
+
+####`StlAllocator.h`
+
+STL allocator wrapping a simple allocate/deallocate interface.
+
+####`String.h`
+
+String utilities that connect `folly::fbstring` with `std::string`.
+
+####`System.h`
+
+Demangling and errno utilities.
+
+####[`ThreadCachedInt.h`](ThreadCachedInt.md)
+
+High-performance atomic increment using thread caching.
+
+####[`ThreadLocal.h`](ThreadLocal.md)
+
+Improved thread local storage for non-trivial types.
+
+####`Traits.h`
+
+Type traits that complement those defined in the standard C++11 header
+`<traits>`.
diff --git a/faux-folly/folly/docs/ThreadCachedInt.md b/faux-folly/folly/docs/ThreadCachedInt.md
new file mode 100644
index 0000000..c4f780e
--- /dev/null
+++ b/faux-folly/folly/docs/ThreadCachedInt.md
@@ -0,0 +1,98 @@
+`folly/ThreadCachedInt.h`
+----------------------
+
+High-performance atomic increment using thread caching.
+
+`folly/ThreadCachedInt.h` introduces a integer class designed for high
+performance increments from multiple threads simultaneously without
+loss of precision.  It has two read modes, `readFast` gives a potentially stale
+value with one load, and `readFull` gives the exact value, but is much slower,
+as discussed below.
+
+
+### Performance
+***
+
+Increment performance is up to 10x greater than `std::atomic_fetch_add` in high
+contention environments.  See `folly/test/ThreadCachedIntTest.h` for more
+comprehensive benchmarks.
+
+`readFast` is as fast as a single load.
+
+`readFull`, on the other hand, requires acquiring a mutex and iterating through
+a list to accumulate the values of all the thread local counters, so is
+significantly slower than `readFast`.
+
+
+### Usage
+***
+
+Create an instance and increment it with `increment` or the operator overloads.
+Read the value with `readFast` for quick, potentially stale data, or `readFull`
+for a more expensive but precise result. There are additional convenience
+functions as well, such as `set`.
+
+``` Cpp
+    ThreadCachedInt<int64_t> val;
+    EXPECT_EQ(0, val.readFast());
+    ++val;                        // increment in thread local counter only
+    EXPECT_EQ(0, val.readFast()); // increment has not been flushed
+    EXPECT_EQ(1, val.readFull()); // accumulates all thread local counters
+    val.set(2);
+    EXPECT_EQ(2, val.readFast());
+    EXPECT_EQ(2, val.readFull());
+```
+
+### Implementation
+***
+
+`folly::ThreadCachedInt` uses `folly::ThreadLocal` to store thread specific
+objects that each have a local counter.  When incrementing, the thread local
+instance is incremented.  If the local counter passes the cache size, the value
+is flushed to the global counter with an atomic increment.  It is this global
+counter that is read with `readFast` via a simple load, but will not count any
+of the updates that haven't been flushed.
+
+In order to read the exact value, `ThreadCachedInt` uses the extended
+`readAllThreads()` API of `folly::ThreadLocal` to iterate through all the
+references to all the associated thread local object instances.  This currently
+requires acquiring a global mutex and iterating through the references,
+accumulating the counters along with the global counter.  This also means that
+the first use of the object from a new thread will acquire the mutex in order to
+insert the thread local reference into the list.  By default, there is one
+global mutex per integer type used in `ThreadCachedInt`.  If you plan on using a
+lot of `ThreadCachedInt`s in your application, considering breaking up the
+global mutex by introducing additional `Tag` template parameters.
+
+`set` simply sets the global counter value, and marks all the thread local
+instances as needing to be reset.  When iterating with `readFull`, thread local
+counters that have been marked as reset are skipped.  When incrementing, thread
+local counters marked for reset are set to zero and unmarked for reset.
+
+Upon destruction, thread local counters are flushed to the parent so that counts
+are not lost after increments in temporary threads.  This requires grabbing the
+global mutex to make sure the parent itself wasn't destroyed in another thread
+already.
+
+### Alternate Implementations
+***
+
+There are of course many ways to skin a cat, and you may notice there is a
+partial alternate implementation in `folly/test/ThreadCachedIntTest.cpp` that
+provides similar performance.  `ShardedAtomicInt` simply uses an array of
+`std::atomic<int64_t>`'s and hashes threads across them to do low-contention
+atomic increments, and `readFull` just sums up all the ints.
+
+This sounds great, but in order to get the contention low enough to get similar
+performance as ThreadCachedInt with 24 threads, `ShardedAtomicInt` needs about
+2000 ints to hash across.  This uses about 20x more memory, and the lock-free
+`readFull` has to sum up all 2048 ints, which ends up being a about 50x slower
+than `ThreadCachedInt` in low contention situations, which is hopefully the
+common case since it's designed for high-write, low read access patterns.
+Performance of `readFull` is about the same speed as `ThreadCachedInt` in high
+contention environments.
+
+Depending on the operating conditions, it may make more sense to use one
+implementation over the other.  For example, a lower contention environment will
+probably be able to use a `ShardedAtomicInt` with a much smaller array without
+hurting performance, while improving memory consumption and perf of `readFull`.
diff --git a/faux-folly/folly/docs/ThreadLocal.md b/faux-folly/folly/docs/ThreadLocal.md
new file mode 100644
index 0000000..65634a3
--- /dev/null
+++ b/faux-folly/folly/docs/ThreadLocal.md
@@ -0,0 +1,105 @@
+`folly/ThreadLocal.h`
+----------------------
+
+Improved thread local storage for non-trivial types.
+
+ * ~4x faster than `boost::thread_specific_ptr`.
+ * Similar speed as using `pthread_getspecific` directly, but only consumes a
+   single `pthread_key_t` per `Tag` template param.
+ * Expands on the `thread_specific_ptr` API with `accessAllThreads` and extended
+   custom deleter support.
+
+
+### Usage
+***
+
+The API of `ThreadLocalPtr` is very close to `boost::thread_specific_ptr` with
+the notable addition of the `accessAllThreads` method.  There is also a
+`ThreadLocal` class which is a thin wrapper around `ThreadLocalPtr` that manages
+allocation automatically (creates a new object the first time it is dereferenced
+from each thread).
+
+`ThreadLocalPtr` simply gives you a place to put and access a pointer local to
+each thread such that it will be destroyed appropriately.
+
+```Cpp
+{
+  folly::ThreadLocalPtr<Widget> w;
+  w.reset(new Widget(0), Widget::customDeleterA);
+  std::thread([&w]() {
+    w.reset(new Widget(1), Widget::customDeleterB);
+    w.get()->mangleWidget();
+  } // Widget(1) is destroyed with customDeleterB
+} // Widget(0) is destroyed with customDeleterA
+```
+
+Note that `customDeleterB` will get called with
+`TLPDestructionMode::THIS_THREAD` and `customerDeleterA` will get called with
+`TLPDestructionMode::ALL_THREADS`.  This is to distinguish between thread exit
+vs. the entire `ThreadLocalPtr` getting destroyed, in which case there is
+cleanup work that may be avoided.
+
+The `accessAllThreads` interface is provided to walk all the thread local child
+objects of a parent.  `accessAllThreads` initializes an accessor
+which holds a global lock that blocks all creation and destruction of
+`ThreadLocal` objects with the same `Tag` and can be used as an iterable
+container. Typical use is for frequent write, infrequent read data access
+patterns such as counters.  Note that you must specify a unique Tag type so you
+don't block other ThreadLocal object usage, and you should try to minimize the
+lifetime of the accessor so the lock is held for as short as possible).
+
+The following example is a simplification of `folly/ThreadCachedInt.h`.  It
+keeps track of a counter value and allows multiple threads to add to the count
+without synchronization.  In order to get the total count, `read()` iterates
+through all the thread local values via `accessAllThreads()` and sums them up.
+`class NewTag` is used to break the global mutex so that this class won't block
+other `ThreadLocal` usage when `read()` is called.
+
+Note that `read()` holds the global mutex which blocks construction,
+destruction, and `read()` for other `SimpleThreadCachedInt`'s, but does not
+block `add()`.  Also, since it uses the unique `NewTag`, `SimpleThreadCachedInt`
+does not affect other `ThreadLocal` usage.
+
+```Cpp
+class SimpleThreadCachedInt {
+
+  class NewTag;  // Segments the global mutex
+  ThreadLocal<int,NewTag> val_;
+
+ public:
+  void add(int val) {
+    *val_ += val;  // operator*() gives a reference to the thread local instance
+  }
+
+  int read() {
+    int ret = 0;
+    // accessAllThreads acquires the global lock
+    for (const auto& i : val_.accessAllThreads()) {
+      ret += i;
+    }  // Global lock is released on scope exit
+    return ret;
+  }
+};
+```
+
+
+### Implementation
+***
+
+We keep a `__thread` array of pointers to objects (`ThreadEntry::elements`)
+where each array has an index for each unique instance of the `ThreadLocalPtr`
+object.  Each `ThreadLocalPtr` object has a unique id that is an index into
+these arrays so we can fetch the correct object from thread local storage
+very efficiently.
+
+In order to prevent unbounded growth of the id space and thus huge
+`ThreadEntry::elements` arrays, for example due to continuous creation and
+destruction of `ThreadLocalPtr` objects, we keep track of all active instances
+by linking them together into a list.  When an instance is destroyed we remove
+it from the chain and insert the id into `freeIds_` for reuse.  These operations
+require a global mutex, but only happen at construction and destruction time.
+`accessAllThreads` also acquires this global mutex.
+
+We use a single global `pthread_key_t` per `Tag` to manage object destruction
+and memory cleanup upon thread exit because there is a finite number of
+`pthread_key_t`'s available per machine.
diff --git a/faux-folly/folly/docs/Traits.md b/faux-folly/folly/docs/Traits.md
new file mode 100644
index 0000000..8d1a0b2
--- /dev/null
+++ b/faux-folly/folly/docs/Traits.md
@@ -0,0 +1,175 @@
+'folly/Traits.h'
+-----------------
+
+Implements traits complementary to those provided in <boost/type_traits.h>
+
+  * Implements `IsRelocatable` trait.
+  * Implements `IsOneOf` trait
+  * Macros to state the assumptions easily
+
+### Motivation
+***
+
+`<boost/type_traits.hpp>` is the Boost type-traits library defining a 
+variety of traits such as `is_integral` or `is_floating_point`. This helps 
+to gain more information about a given type.
+Many traits introduced by Boost have been standardized in C++11.
+
+`folly/Traits.h` implements traits complementing those present in boost. 
+
+
+### IsRelocatable
+***
+
+In C++, the default way to move an object is by 
+calling the copy constructor and destroying the old copy 
+instead of directly copying the memory contents by using memcpy(). 
+The conservative approach of moving an object assumes that the copied 
+object is not relocatable. 
+The two following code sequences should be semantically equivalent for a
+relocatable type:
+
+```Cpp
+{
+  void conservativeMove(T * from, T * to) {
+    new(to) T(from);
+    (*from).~T();
+  }
+}
+
+{
+  void optimizedMove(T * from, T * to) {
+    memcpy(to, from, sizeof(T));
+  }
+}
+```
+
+Very few C++ types are non-relocatable.
+The type defined below maintains a pointer inside an embedded buffer and 
+hence would be non-relocatable. Moving the object by simply copying its 
+memory contents would leave the internal pointer pointing to the old buffer.
+
+```Cpp
+class NonRelocatableType {
+private:
+  char buffer[1024];
+  char * pointerToBuffer;
+  ...
+public:
+  NonRelocatableType() : pointerToBuffer(buffer) {}
+  ...
+};
+```
+
+We can optimize the task of moving a relocatable type T using memcpy. 
+IsRelocatable<T>::value describes the ability of moving around memory 
+a value of type T by using memcpy.
+
+### Usage
+***
+
+  * Declaring types
+
+    ```Cpp
+    template <class T1, class T2>
+    class MyParameterizedType;
+
+    class MySimpleType;
+    ```
+
+  * Declaring a type as relocatable
+
+    Appending the lines below after definition of My*Type 
+    (`MyParameterizedType` or `MySimpleType`) will declare it as relocatable
+
+    ```Cpp
+    /* Definition of My*Type goes here */
+    // global namespace (not inside any namespace)
+    namespace folly {
+      // defining specialization of IsRelocatable for MySimpleType
+      template <>
+      struct IsRelocatable<MySimpleType> : boost::true_type {};
+      // defining specialization of IsRelocatable for MyParameterizedType
+      template <class T1, class T2>
+      struct IsRelocatable<MyParameterizedType<T1, T2>>
+          : ::boost::true_type {};
+    }
+    ```
+
+  * To make it easy to state assumptions for a regular type or a family of 
+    parameterized type, various macros can be used as shown below.
+
+  * Stating that a type is Relocatable using a macro
+
+    ```Cpp
+    // global namespace
+    namespace folly {
+      // For a Regular Type
+      FOLLY_ASSUME_RELOCATABLE(MySimpleType);
+      // For a Parameterized Type
+      FOLLY_ASSUME_RELOCATABLE(MyParameterizedType<T1, T2>);
+    }
+    ```
+
+  * Stating that a type has no throw constructor using a macro
+
+    ```Cpp
+    namespace boost {
+      // For a Regular Type
+      FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MySimpleType);
+      // For a Parameterized Type
+      FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MyParameterizedType<T1, T2>);
+    }
+    ```
+
+`fbvector` only works with relocatable objects. If assumptions are not stated 
+explicitly, `fbvector<MySimpleType>` or `fbvector<MyParameterizedType>` 
+will fail to compile due to assertion below:
+
+```Cpp
+BOOST_STATIC_ASSERT(
+  IsRelocatable<My*Type>::value
+);
+```
+
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE*(type) macros can be used to state that type 
+is relocatable and has nothrow constructor.
+
+  * Stating that a type is `fbvector-compatible` using macros
+    i.e. relocatable and has nothrow default constructor
+
+    ```Cpp
+    // at global level, i.e no namespace
+    // macro for regular type
+    FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MySimpleType);
+    // macro for types having 2 template parameters (MyParameterizedType)
+    FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyParameterizedType);
+    ```
+
+Similarly, 
+
+  * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(MyTypeHavingOneParameter) macro is 
+    for family of parameterized types having 1 parameter
+
+  * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(MyTypeHavingThreeParameters) macro is 
+    for family of parameterized types having 3 parameters
+
+  * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(MyTypeHavingFourParameters) macro is 
+    for family of parameterized types having 4 parameters
+
+Few common types, namely `std::basic_string`, `std::vector`, `std::list`,
+`std::map`, `std::deque`, `std::set`, `std::unique_ptr`, `std::shared_ptr`,
+`std::function`, `boost::shared_ptr` which are compatible with `fbvector` are
+already instantiated and declared compatible with `fbvector`. `fbvector` can be
+directly used with any of these C++ types.
+
+`std::pair` can be safely assumed to be compatible with `fbvector` if both of
+its components are.
+
+### IsOneOf
+***
+
+`boost::is_same<T1, T2>::value` can be used to test if types of T1 and T2 are 
+same. `folly::IsOneOf<T, T1, Ts...>::value` can be used to test if type of T1 
+matches the type of one of the other template parameter, T1, T2, ...Tn.
+Recursion is used to implement this type trait.
diff --git a/faux-folly/folly/docs/style.css b/faux-folly/folly/docs/style.css
new file mode 100644
index 0000000..67ac7e7
--- /dev/null
+++ b/faux-folly/folly/docs/style.css
@@ -0,0 +1,7 @@
+<style type="text/css">
+pre.literal-block, pre.doctest-block, pre.sourceCode {
+    margin-left: 2em;
+    margin-right: 2em;
+    background-color: #eeeeee
+}
+</style>
diff --git a/faux-folly/folly/dynamic-inl.h b/faux-folly/folly/dynamic-inl.h
new file mode 100644
index 0000000..293fd98
--- /dev/null
+++ b/faux-folly/folly/dynamic-inl.h
@@ -0,0 +1,807 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_DYNAMIC_INL_H_
+#define FOLLY_DYNAMIC_INL_H_
+
+#include <functional>
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include <folly/Likely.h>
+#include <folly/Conv.h>
+#include <folly/Format.h>
+
+//////////////////////////////////////////////////////////////////////
+
+namespace std {
+
+template<>
+struct hash< ::folly::dynamic> {
+  size_t operator()(::folly::dynamic const& d) const {
+    return d.hash();
+  }
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+// This is a higher-order preprocessor macro to aid going from runtime
+// types to the compile time type system.
+#define FB_DYNAMIC_APPLY(type, apply) do {         \
+  switch ((type)) {                             \
+  case NULLT:   apply(void*);          break;   \
+  case ARRAY:   apply(Array);          break;   \
+  case BOOL:    apply(bool);           break;   \
+  case DOUBLE:  apply(double);         break;   \
+  case INT64:   apply(int64_t);        break;   \
+  case OBJECT:  apply(ObjectImpl);     break;   \
+  case STRING:  apply(fbstring);       break;   \
+  default:      CHECK(0); abort();              \
+  }                                             \
+} while (0)
+
+//////////////////////////////////////////////////////////////////////
+
+namespace folly {
+
+struct TypeError : std::runtime_error {
+  explicit TypeError(const std::string& expected, dynamic::Type actual);
+  explicit TypeError(const std::string& expected,
+    dynamic::Type actual1, dynamic::Type actual2);
+  ~TypeError();
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+namespace detail {
+
+  // This helper is used in destroy() to be able to run destructors on
+  // types like "int64_t" without a compiler error.
+  struct Destroy {
+    template<class T> static void destroy(T* t) { t->~T(); }
+  };
+
+  /*
+   * The enable_if junk here is necessary to avoid ambiguous
+   * conversions relating to bool and double when you implicitly
+   * convert an int or long to a dynamic.
+   */
+  template<class T, class Enable = void> struct ConversionHelper;
+  template<class T>
+  struct ConversionHelper<
+    T,
+    typename std::enable_if<
+      std::is_integral<T>::value && !std::is_same<T,bool>::value
+    >::type
+  > {
+    typedef int64_t type;
+  };
+  template<class T>
+  struct ConversionHelper<
+    T,
+    typename std::enable_if<
+      (!std::is_integral<T>::value || std::is_same<T,bool>::value) &&
+      !std::is_same<T,std::nullptr_t>::value
+    >::type
+  > {
+    typedef T type;
+  };
+  template<class T>
+  struct ConversionHelper<
+    T,
+    typename std::enable_if<
+      std::is_same<T,std::nullptr_t>::value
+    >::type
+  > {
+    typedef void* type;
+  };
+
+  /*
+   * Helper for implementing numeric conversions in operators on
+   * numbers.  Just promotes to double when one of the arguments is
+   * double, or throws if either is not a numeric type.
+   */
+  template<template<class> class Op>
+  dynamic numericOp(dynamic const& a, dynamic const& b) {
+    if (!a.isNumber() || !b.isNumber()) {
+      throw TypeError("numeric", a.type(), b.type());
+    }
+    if (a.type() != b.type()) {
+      auto& integ  = a.isInt() ? a : b;
+      auto& nonint = a.isInt() ? b : a;
+      return Op<double>()(to<double>(integ.asInt()), nonint.asDouble());
+    }
+    if (a.isDouble()) {
+      return Op<double>()(a.asDouble(), b.asDouble());
+    }
+    return Op<int64_t>()(a.asInt(), b.asInt());
+  }
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+/*
+ * We're doing this instead of a simple member typedef to avoid the
+ * undefined behavior of parameterizing std::unordered_map<> with an
+ * incomplete type.
+ *
+ * Note: Later we may add separate order tracking here (a multi-index
+ * type of thing.)
+ */
+struct dynamic::ObjectImpl : std::unordered_map<dynamic, dynamic> {};
+
+//////////////////////////////////////////////////////////////////////
+
+// Helper object for creating objects conveniently.  See object and
+// the dynamic::dynamic(ObjectMaker&&) ctor.
+struct dynamic::ObjectMaker {
+  friend struct dynamic;
+
+  explicit ObjectMaker() : val_(dynamic::object) {}
+  explicit ObjectMaker(dynamic const& key, dynamic val)
+    : val_(dynamic::object)
+  {
+    val_.insert(key, std::move(val));
+  }
+  explicit ObjectMaker(dynamic&& key, dynamic val)
+    : val_(dynamic::object)
+  {
+    val_.insert(std::move(key), std::move(val));
+  }
+
+  // Make sure no one tries to save one of these into an lvalue with
+  // auto or anything like that.
+  ObjectMaker(ObjectMaker&&) = default;
+  ObjectMaker(ObjectMaker const&) = delete;
+  ObjectMaker& operator=(ObjectMaker const&) = delete;
+  ObjectMaker& operator=(ObjectMaker&&) = delete;
+
+  // These return rvalue-references instead of lvalue-references to allow
+  // constructs like this to moved instead of copied:
+  //  dynamic a = dynamic::object("a", "b")("c", "d")
+  ObjectMaker&& operator()(dynamic const& key, dynamic val) {
+    val_.insert(key, std::move(val));
+    return std::move(*this);
+  }
+
+  ObjectMaker&& operator()(dynamic&& key, dynamic val) {
+    val_.insert(std::move(key), std::move(val));
+    return std::move(*this);
+  }
+
+private:
+  dynamic val_;
+};
+
+// This looks like a case for perfect forwarding, but our use of
+// std::initializer_list for constructing dynamic arrays makes it less
+// functional than doing this manually.
+inline dynamic::ObjectMaker dynamic::object() { return ObjectMaker(); }
+inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic&& b) {
+  return ObjectMaker(std::move(a), std::move(b));
+}
+inline dynamic::ObjectMaker dynamic::object(dynamic const& a, dynamic&& b) {
+  return ObjectMaker(a, std::move(b));
+}
+inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic const& b) {
+  return ObjectMaker(std::move(a), b);
+}
+inline dynamic::ObjectMaker
+dynamic::object(dynamic const& a, dynamic const& b) {
+  return ObjectMaker(a, b);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+struct dynamic::const_item_iterator
+  : boost::iterator_adaptor<dynamic::const_item_iterator,
+                            dynamic::ObjectImpl::const_iterator> {
+  /* implicit */ const_item_iterator(base_type b) : iterator_adaptor_(b) { }
+
+ private:
+  friend class boost::iterator_core_access;
+};
+
+struct dynamic::const_key_iterator
+  : boost::iterator_adaptor<dynamic::const_key_iterator,
+                            dynamic::ObjectImpl::const_iterator,
+                            dynamic const> {
+  /* implicit */ const_key_iterator(base_type b) : iterator_adaptor_(b) { }
+
+ private:
+  dynamic const& dereference() const {
+    return base_reference()->first;
+  }
+  friend class boost::iterator_core_access;
+};
+
+struct dynamic::const_value_iterator
+  : boost::iterator_adaptor<dynamic::const_value_iterator,
+                            dynamic::ObjectImpl::const_iterator,
+                            dynamic const> {
+  /* implicit */ const_value_iterator(base_type b) : iterator_adaptor_(b) { }
+
+ private:
+  dynamic const& dereference() const {
+    return base_reference()->second;
+  }
+  friend class boost::iterator_core_access;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+inline dynamic::dynamic(ObjectMaker (*)())
+  : type_(OBJECT)
+{
+  new (getAddress<ObjectImpl>()) ObjectImpl();
+}
+
+inline dynamic::dynamic(StringPiece s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(s.data(), s.size());
+}
+
+inline dynamic::dynamic(char const* s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(s);
+}
+
+inline dynamic::dynamic(std::string const& s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(s);
+}
+
+inline dynamic::dynamic(fbstring const& s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(s);
+}
+
+inline dynamic::dynamic(fbstring&& s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(std::move(s));
+}
+
+inline dynamic::dynamic(std::initializer_list<dynamic> il)
+  : type_(ARRAY)
+{
+  new (&u_.array) Array(il.begin(), il.end());
+}
+
+inline dynamic::dynamic(ObjectMaker&& maker)
+  : type_(OBJECT)
+{
+  new (getAddress<ObjectImpl>())
+    ObjectImpl(std::move(*maker.val_.getAddress<ObjectImpl>()));
+}
+
+inline dynamic::dynamic(dynamic const& o)
+  : type_(NULLT)
+{
+  *this = o;
+}
+
+inline dynamic::dynamic(dynamic&& o) noexcept
+  : type_(NULLT)
+{
+  *this = std::move(o);
+}
+
+inline dynamic::~dynamic() noexcept { destroy(); }
+
+template<class T>
+dynamic::dynamic(T t) {
+  typedef typename detail::ConversionHelper<T>::type U;
+  type_ = TypeInfo<U>::type;
+  new (getAddress<U>()) U(std::move(t));
+}
+
+template<class Iterator>
+dynamic::dynamic(Iterator first, Iterator last)
+  : type_(ARRAY)
+{
+  new (&u_.array) Array(first, last);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+inline dynamic::const_iterator dynamic::begin() const {
+  return get<Array>().begin();
+}
+inline dynamic::const_iterator dynamic::end() const {
+  return get<Array>().end();
+}
+
+template <class It>
+struct dynamic::IterableProxy {
+  typedef It const_iterator;
+  typedef typename It::value_type value_type;
+
+  /* implicit */ IterableProxy(const dynamic::ObjectImpl* o) : o_(o) { }
+
+  It begin() const {
+    return o_->begin();
+  }
+
+  It end() const {
+    return o_->end();
+  }
+
+ private:
+  const dynamic::ObjectImpl* o_;
+};
+
+inline dynamic::IterableProxy<dynamic::const_key_iterator> dynamic::keys()
+  const {
+  return &(get<ObjectImpl>());
+}
+
+inline dynamic::IterableProxy<dynamic::const_value_iterator> dynamic::values()
+  const {
+  return &(get<ObjectImpl>());
+}
+
+inline dynamic::IterableProxy<dynamic::const_item_iterator> dynamic::items()
+  const {
+  return &(get<ObjectImpl>());
+}
+
+inline bool dynamic::isString() const { return get_nothrow<fbstring>(); }
+inline bool dynamic::isObject() const { return get_nothrow<ObjectImpl>(); }
+inline bool dynamic::isBool()   const { return get_nothrow<bool>(); }
+inline bool dynamic::isArray()  const { return get_nothrow<Array>(); }
+inline bool dynamic::isDouble() const { return get_nothrow<double>(); }
+inline bool dynamic::isInt()    const { return get_nothrow<int64_t>(); }
+inline bool dynamic::isNull()   const { return get_nothrow<void*>(); }
+inline bool dynamic::isNumber() const { return isInt() || isDouble(); }
+
+inline dynamic::Type dynamic::type() const {
+  return type_;
+}
+
+inline fbstring dynamic::asString() const { return asImpl<fbstring>(); }
+inline double   dynamic::asDouble() const { return asImpl<double>(); }
+inline int64_t  dynamic::asInt()    const { return asImpl<int64_t>(); }
+inline bool     dynamic::asBool()   const { return asImpl<bool>(); }
+
+inline const fbstring& dynamic::getString() const& { return get<fbstring>(); }
+inline double          dynamic::getDouble() const& { return get<double>(); }
+inline int64_t         dynamic::getInt()    const& { return get<int64_t>(); }
+inline bool            dynamic::getBool()   const& { return get<bool>(); }
+
+inline fbstring& dynamic::getString() & { return get<fbstring>(); }
+inline double&   dynamic::getDouble() & { return get<double>(); }
+inline int64_t&  dynamic::getInt()    & { return get<int64_t>(); }
+inline bool&     dynamic::getBool()   & { return get<bool>(); }
+
+inline fbstring dynamic::getString() && { return std::move(get<fbstring>()); }
+inline double   dynamic::getDouble() && { return get<double>(); }
+inline int64_t  dynamic::getInt()    && { return get<int64_t>(); }
+inline bool     dynamic::getBool()   && { return get<bool>(); }
+
+inline const char* dynamic::data()  const& { return get<fbstring>().data();  }
+inline const char* dynamic::c_str() const& { return get<fbstring>().c_str(); }
+inline StringPiece dynamic::stringPiece() const { return get<fbstring>(); }
+
+template<class T>
+struct dynamic::CompareOp {
+  static bool comp(T const& a, T const& b) { return a < b; }
+};
+template<>
+struct dynamic::CompareOp<dynamic::ObjectImpl> {
+  static bool comp(ObjectImpl const&, ObjectImpl const&) {
+    // This code never executes; it is just here for the compiler.
+    return false;
+  }
+};
+
+inline dynamic& dynamic::operator+=(dynamic const& o) {
+  if (type() == STRING && o.type() == STRING) {
+    *getAddress<fbstring>() += *o.getAddress<fbstring>();
+    return *this;
+  }
+  *this = detail::numericOp<std::plus>(*this, o);
+  return *this;
+}
+
+inline dynamic& dynamic::operator-=(dynamic const& o) {
+  *this = detail::numericOp<std::minus>(*this, o);
+  return *this;
+}
+
+inline dynamic& dynamic::operator*=(dynamic const& o) {
+  *this = detail::numericOp<std::multiplies>(*this, o);
+  return *this;
+}
+
+inline dynamic& dynamic::operator/=(dynamic const& o) {
+  *this = detail::numericOp<std::divides>(*this, o);
+  return *this;
+}
+
+#define FB_DYNAMIC_INTEGER_OP(op)                           \
+  inline dynamic& dynamic::operator op(dynamic const& o) {  \
+    if (!isInt() || !o.isInt()) {                           \
+      throw TypeError("int64", type(), o.type());           \
+    }                                                       \
+    *getAddress<int64_t>() op o.asInt();                    \
+    return *this;                                           \
+  }
+
+FB_DYNAMIC_INTEGER_OP(%=)
+FB_DYNAMIC_INTEGER_OP(|=)
+FB_DYNAMIC_INTEGER_OP(&=)
+FB_DYNAMIC_INTEGER_OP(^=)
+
+#undef FB_DYNAMIC_INTEGER_OP
+
+inline dynamic& dynamic::operator++() {
+  ++get<int64_t>();
+  return *this;
+}
+
+inline dynamic& dynamic::operator--() {
+  --get<int64_t>();
+  return *this;
+}
+
+inline dynamic const& dynamic::operator[](dynamic const& idx) const& {
+  return at(idx);
+}
+
+inline dynamic dynamic::operator[](dynamic const& idx) && {
+  return std::move((*this)[idx]);
+}
+
+template<class K, class V> inline dynamic& dynamic::setDefault(K&& k, V&& v) {
+  auto& obj = get<ObjectImpl>();
+  return obj.insert(std::make_pair(std::forward<K>(k),
+                                   std::forward<V>(v))).first->second;
+}
+
+inline dynamic* dynamic::get_ptr(dynamic const& idx) & {
+  return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx));
+}
+
+inline dynamic& dynamic::at(dynamic const& idx) & {
+  return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx));
+}
+
+inline dynamic dynamic::at(dynamic const& idx) && {
+  return std::move(at(idx));
+}
+
+inline bool dynamic::empty() const {
+  if (isNull()) {
+    return true;
+  }
+  return !size();
+}
+
+inline std::size_t dynamic::count(dynamic const& key) const {
+  return find(key) != items().end();
+}
+
+inline dynamic::const_item_iterator dynamic::find(dynamic const& key) const {
+  return get<ObjectImpl>().find(key);
+}
+
+template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
+  auto& obj = get<ObjectImpl>();
+  auto rv = obj.insert({ std::forward<K>(key), nullptr });
+  rv.first->second = std::forward<V>(val);
+}
+
+inline std::size_t dynamic::erase(dynamic const& key) {
+  auto& obj = get<ObjectImpl>();
+  return obj.erase(key);
+}
+
+inline dynamic::const_iterator dynamic::erase(const_iterator it) {
+  auto& arr = get<Array>();
+  // std::vector doesn't have an erase method that works on const iterators,
+  // even though the standard says it should, so this hack converts to a
+  // non-const iterator before calling erase.
+  return get<Array>().erase(arr.begin() + (it - arr.begin()));
+}
+
+inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) {
+  return const_key_iterator(get<ObjectImpl>().erase(it.base()));
+}
+
+inline dynamic::const_key_iterator dynamic::erase(const_key_iterator first,
+                                                  const_key_iterator last) {
+  return const_key_iterator(get<ObjectImpl>().erase(first.base(),
+                                                    last.base()));
+}
+
+inline dynamic::const_value_iterator dynamic::erase(const_value_iterator it) {
+  return const_value_iterator(get<ObjectImpl>().erase(it.base()));
+}
+
+inline dynamic::const_value_iterator dynamic::erase(const_value_iterator first,
+                                                    const_value_iterator last) {
+  return const_value_iterator(get<ObjectImpl>().erase(first.base(),
+                                                      last.base()));
+}
+
+inline dynamic::const_item_iterator dynamic::erase(const_item_iterator it) {
+  return const_item_iterator(get<ObjectImpl>().erase(it.base()));
+}
+
+inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first,
+                                                   const_item_iterator last) {
+  return const_item_iterator(get<ObjectImpl>().erase(first.base(),
+                                                     last.base()));
+}
+
+inline void dynamic::resize(std::size_t sz, dynamic const& c) {
+  auto& array = get<Array>();
+  array.resize(sz, c);
+}
+
+inline void dynamic::push_back(dynamic const& v) {
+  auto& array = get<Array>();
+  array.push_back(v);
+}
+
+inline void dynamic::push_back(dynamic&& v) {
+  auto& array = get<Array>();
+  array.push_back(std::move(v));
+}
+
+inline void dynamic::pop_back() {
+  auto& array = get<Array>();
+  array.pop_back();
+}
+
+//////////////////////////////////////////////////////////////////////
+
+template<class T> struct dynamic::TypeInfo {
+  static char const name[];
+  static Type const type;
+};
+
+#define FB_DEC_TYPE(T)                                      \
+  template<> char const dynamic::TypeInfo<T>::name[];       \
+  template<> dynamic::Type const dynamic::TypeInfo<T>::type
+
+FB_DEC_TYPE(void*);
+FB_DEC_TYPE(bool);
+FB_DEC_TYPE(fbstring);
+FB_DEC_TYPE(dynamic::Array);
+FB_DEC_TYPE(double);
+FB_DEC_TYPE(int64_t);
+FB_DEC_TYPE(dynamic::ObjectImpl);
+
+#undef FB_DEC_TYPE
+
+template<class T>
+T dynamic::asImpl() const {
+  switch (type()) {
+  case INT64:    return to<T>(*get_nothrow<int64_t>());
+  case DOUBLE:   return to<T>(*get_nothrow<double>());
+  case BOOL:     return to<T>(*get_nothrow<bool>());
+  case STRING:   return to<T>(*get_nothrow<fbstring>());
+  default:
+    throw TypeError("int/double/bool/string", type());
+  }
+}
+
+// Return a T* to our type, or null if we're not that type.
+template<class T>
+T* dynamic::get_nothrow() & noexcept {
+  if (type_ != TypeInfo<T>::type) {
+    return nullptr;
+  }
+  return getAddress<T>();
+}
+
+template<class T>
+T const* dynamic::get_nothrow() const& noexcept {
+  return const_cast<dynamic*>(this)->get_nothrow<T>();
+}
+
+// Return T* for where we can put a T, without type checking.  (Memory
+// might be uninitialized, even.)
+template<class T>
+T* dynamic::getAddress() noexcept {
+  return GetAddrImpl<T>::get(u_);
+}
+
+template<class T>
+T const* dynamic::getAddress() const noexcept {
+  return const_cast<dynamic*>(this)->getAddress<T>();
+}
+
+template<class T> struct dynamic::GetAddrImpl {};
+template<> struct dynamic::GetAddrImpl<void*> {
+  static void** get(Data& d) noexcept { return &d.nul; }
+};
+template<> struct dynamic::GetAddrImpl<dynamic::Array> {
+  static Array* get(Data& d) noexcept { return &d.array; }
+};
+template<> struct dynamic::GetAddrImpl<bool> {
+  static bool* get(Data& d) noexcept { return &d.boolean; }
+};
+template<> struct dynamic::GetAddrImpl<int64_t> {
+  static int64_t* get(Data& d) noexcept { return &d.integer; }
+};
+template<> struct dynamic::GetAddrImpl<double> {
+  static double* get(Data& d) noexcept { return &d.doubl; }
+};
+template<> struct dynamic::GetAddrImpl<fbstring> {
+  static fbstring* get(Data& d) noexcept { return &d.string; }
+};
+template<> struct dynamic::GetAddrImpl<dynamic::ObjectImpl> {
+  static_assert(sizeof(ObjectImpl) <= sizeof(Data::objectBuffer),
+    "In your implementation, std::unordered_map<> apparently takes different"
+    " amount of space depending on its template parameters.  This is "
+    "weird.  Make objectBuffer bigger if you want to compile dynamic.");
+
+  static ObjectImpl* get(Data& d) noexcept {
+    void* data = &d.objectBuffer;
+    return static_cast<ObjectImpl*>(data);
+  }
+};
+
+template<class T>
+T& dynamic::get() {
+  if (auto* p = get_nothrow<T>()) {
+    return *p;
+  }
+  throw TypeError(TypeInfo<T>::name, type());
+}
+
+template<class T>
+T const& dynamic::get() const {
+  return const_cast<dynamic*>(this)->get<T>();
+}
+
+//////////////////////////////////////////////////////////////////////
+
+/*
+ * Helper for implementing operator<<.  Throws if the type shouldn't
+ * support it.
+ */
+template<class T>
+struct dynamic::PrintImpl {
+  static void print(dynamic const&, std::ostream& out, T const& t) {
+    out << t;
+  }
+};
+template<>
+struct dynamic::PrintImpl<dynamic::ObjectImpl> {
+  static void print(dynamic const& d,
+                    std::ostream& out,
+                    dynamic::ObjectImpl const&) {
+  }
+};
+template<>
+struct dynamic::PrintImpl<dynamic::Array> {
+  static void print(dynamic const& d,
+                    std::ostream& out,
+                    dynamic::Array const&) {
+  }
+};
+
+inline void dynamic::print(std::ostream& out) const {
+#define FB_X(T) PrintImpl<T>::print(*this, out, *getAddress<T>())
+  FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+}
+
+inline std::ostream& operator<<(std::ostream& out, dynamic const& d) {
+  d.print(out);
+  return out;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+// Secialization of FormatValue so dynamic objects can be formatted
+template <>
+class FormatValue<dynamic> {
+ public:
+  explicit FormatValue(const dynamic& val) : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    switch (val_.type()) {
+    case dynamic::NULLT:
+      FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
+      break;
+    case dynamic::BOOL:
+      FormatValue<bool>(val_.asBool()).format(arg, cb);
+      break;
+    case dynamic::INT64:
+      FormatValue<int64_t>(val_.asInt()).format(arg, cb);
+      break;
+    case dynamic::STRING:
+      FormatValue<fbstring>(val_.asString()).format(arg, cb);
+      break;
+    case dynamic::DOUBLE:
+      FormatValue<double>(val_.asDouble()).format(arg, cb);
+      break;
+    case dynamic::ARRAY:
+      FormatValue(val_.at(arg.splitIntKey())).format(arg, cb);
+      break;
+    case dynamic::OBJECT:
+      FormatValue(val_.at(arg.splitKey().toFbstring())).format(arg, cb);
+      break;
+    }
+  }
+
+ private:
+  const dynamic& val_;
+};
+
+template <class V>
+class FormatValue<detail::DefaultValueWrapper<dynamic, V>> {
+ public:
+  explicit FormatValue(
+      const detail::DefaultValueWrapper<dynamic, V>& val)
+    : val_(val) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    auto& c = val_.container;
+    switch (c.type()) {
+    case dynamic::NULLT:
+    case dynamic::BOOL:
+    case dynamic::INT64:
+    case dynamic::STRING:
+    case dynamic::DOUBLE:
+      FormatValue<dynamic>(c).format(arg, cb);
+      break;
+    case dynamic::ARRAY:
+      {
+        int key = arg.splitIntKey();
+        if (key >= 0 && size_t(key) < c.size()) {
+          FormatValue<dynamic>(c.at(key)).format(arg, cb);
+        } else{
+          FormatValue<V>(val_.defaultValue).format(arg, cb);
+        }
+      }
+      break;
+    case dynamic::OBJECT:
+      {
+        auto pos = c.find(arg.splitKey());
+        if (pos != c.items().end()) {
+          FormatValue<dynamic>(pos->second).format(arg, cb);
+        } else {
+          FormatValue<V>(val_.defaultValue).format(arg, cb);
+        }
+      }
+      break;
+    }
+  }
+
+ private:
+  const detail::DefaultValueWrapper<dynamic, V>& val_;
+};
+
+}  // namespaces
+
+#undef FB_DYNAMIC_APPLY
+
+#endif
diff --git a/faux-folly/folly/dynamic.cpp b/faux-folly/folly/dynamic.cpp
new file mode 100644
index 0000000..d743bb2
--- /dev/null
+++ b/faux-folly/folly/dynamic.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2015 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/dynamic.h>
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+#define DEF_TYPE(T, str, typen)                                 \
+  template<> char const dynamic::TypeInfo<T>::name[] = str;       \
+  template<> dynamic::Type const dynamic::TypeInfo<T>::type = typen
+
+DEF_TYPE(void*,               "null",    dynamic::NULLT);
+DEF_TYPE(bool,                "boolean", dynamic::BOOL);
+DEF_TYPE(fbstring,            "string",  dynamic::STRING);
+DEF_TYPE(dynamic::Array,      "array",   dynamic::ARRAY);
+DEF_TYPE(double,              "double",  dynamic::DOUBLE);
+DEF_TYPE(int64_t,             "int64",   dynamic::INT64);
+DEF_TYPE(dynamic::ObjectImpl, "object",  dynamic::OBJECT);
+
+#undef DEF_TYPE
+
+const char* dynamic::typeName() const {
+  return typeName(type_);
+}
+
+TypeError::TypeError(const std::string& expected, dynamic::Type actual)
+  : std::runtime_error(to<std::string>("TypeError: expected dynamic "
+      "type `", expected, '\'', ", but had type `",
+      dynamic::typeName(actual), '\''))
+{}
+
+TypeError::TypeError(const std::string& expected,
+    dynamic::Type actual1, dynamic::Type actual2)
+  : std::runtime_error(to<std::string>("TypeError: expected dynamic "
+      "types `", expected, '\'', ", but had types `",
+      dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
+      '\''))
+{}
+
+TypeError::~TypeError() = default;
+
+// This is a higher-order preprocessor macro to aid going from runtime
+// types to the compile time type system.
+#define FB_DYNAMIC_APPLY(type, apply) do {         \
+  switch ((type)) {                             \
+  case NULLT:   apply(void*);          break;   \
+  case ARRAY:   apply(Array);          break;   \
+  case BOOL:    apply(bool);           break;   \
+  case DOUBLE:  apply(double);         break;   \
+  case INT64:   apply(int64_t);        break;   \
+  case OBJECT:  apply(ObjectImpl);     break;   \
+  case STRING:  apply(fbstring);       break;   \
+  default:      CHECK(0); abort();              \
+  }                                             \
+} while (0)
+
+bool dynamic::operator<(dynamic const& o) const {
+  if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
+    throw TypeError("object", type_);
+  }
+  if (type_ != o.type_) {
+    return type_ < o.type_;
+  }
+
+#define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(),   \
+                                          *o.getAddress<T>())
+  FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+}
+
+bool dynamic::operator==(dynamic const& o) const {
+  if (type() != o.type()) {
+    if (isNumber() && o.isNumber()) {
+      auto& integ = isInt() ? *this : o;
+      auto& doubl = isInt() ? o     : *this;
+      return integ.asInt() == doubl.asDouble();
+    }
+    return false;
+  }
+
+#define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
+  FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+}
+
+dynamic& dynamic::operator=(dynamic const& o) {
+  if (&o != this) {
+    if (type_ == o.type_) {
+#define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
+      FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+    } else {
+      destroy();
+#define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
+      FB_DYNAMIC_APPLY(o.type_, FB_X);
+#undef FB_X
+      type_ = o.type_;
+    }
+  }
+  return *this;
+}
+
+dynamic& dynamic::operator=(dynamic&& o) noexcept {
+  if (&o != this) {
+    if (type_ == o.type_) {
+#define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
+      FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+    } else {
+      destroy();
+#define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
+      FB_DYNAMIC_APPLY(o.type_, FB_X);
+#undef FB_X
+      type_ = o.type_;
+    }
+  }
+  return *this;
+}
+
+dynamic& dynamic::operator[](dynamic const& k) & {
+  if (!isObject() && !isArray()) {
+    throw TypeError("object/array", type());
+  }
+  if (isArray()) {
+    return at(k);
+  }
+  auto& obj = get<ObjectImpl>();
+  auto ret = obj.insert({k, nullptr});
+  return ret.first->second;
+}
+
+dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
+  auto& obj = get<ObjectImpl>();
+  auto it = obj.find(k);
+  return it == obj.end() ? v : it->second;
+}
+
+dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
+  auto& obj = get<ObjectImpl>();
+  auto it = obj.find(k);
+  // Avoid clang bug with ternary
+  if (it == obj.end()) {
+    return std::move(v);
+  } else {
+    return it->second;
+  }
+}
+
+dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
+  auto& obj = get<ObjectImpl>();
+  auto it = obj.find(k);
+  // Avoid clang bug with ternary
+  if (it == obj.end()) {
+    return v;
+  } else {
+    return std::move(it->second);
+  }
+}
+
+dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
+  auto& obj = get<ObjectImpl>();
+  auto it = obj.find(k);
+  return std::move(it == obj.end() ? v : it->second);
+}
+
+const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
+  if (auto* parray = get_nothrow<Array>()) {
+    if (!idx.isInt()) {
+      throw TypeError("int64", idx.type());
+    }
+    if (idx < 0 || idx >= parray->size()) {
+      return nullptr;
+    }
+    return &(*parray)[idx.asInt()];
+  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
+    auto it = pobject->find(idx);
+    if (it == pobject->end()) {
+      return nullptr;
+    }
+    return &it->second;
+  } else {
+    throw TypeError("object/array", type());
+  }
+}
+
+dynamic const& dynamic::at(dynamic const& idx) const& {
+  if (auto* parray = get_nothrow<Array>()) {
+    if (!idx.isInt()) {
+      throw TypeError("int64", idx.type());
+    }
+    if (idx < 0 || idx >= parray->size()) {
+      throw std::out_of_range("out of range in dynamic array");
+    }
+    return (*parray)[idx.asInt()];
+  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
+    auto it = pobject->find(idx);
+    if (it == pobject->end()) {
+      throw std::out_of_range(to<std::string>(
+          "couldn't find key ", idx.asString(), " in dynamic object"));
+    }
+    return it->second;
+  } else {
+    throw TypeError("object/array", type());
+  }
+}
+
+std::size_t dynamic::size() const {
+  if (auto* ar = get_nothrow<Array>()) {
+    return ar->size();
+  }
+  if (auto* obj = get_nothrow<ObjectImpl>()) {
+    return obj->size();
+  }
+  if (auto* str = get_nothrow<fbstring>()) {
+    return str->size();
+  }
+  throw TypeError("array/object", type());
+}
+
+dynamic::const_iterator
+dynamic::erase(const_iterator first, const_iterator last) {
+  auto& arr = get<Array>();
+  return get<Array>().erase(
+    arr.begin() + (first - arr.begin()),
+    arr.begin() + (last - arr.begin()));
+}
+
+std::size_t dynamic::hash() const {
+  switch (type()) {
+  case OBJECT:
+  case ARRAY:
+  case NULLT:
+    throw TypeError("not null/object/array", type());
+  case INT64:
+    return std::hash<int64_t>()(asInt());
+  case DOUBLE:
+    return std::hash<double>()(asDouble());
+  case BOOL:
+    return std::hash<bool>()(asBool());
+  case STRING:
+    return std::hash<fbstring>()(asString());
+  default:
+    CHECK(0); abort();
+  }
+}
+
+char const* dynamic::typeName(Type t) {
+#define FB_X(T) return TypeInfo<T>::name
+  FB_DYNAMIC_APPLY(t, FB_X);
+#undef FB_X
+}
+
+void dynamic::destroy() noexcept {
+  // This short-circuit speeds up some microbenchmarks.
+  if (type_ == NULLT) return;
+
+#define FB_X(T) detail::Destroy::destroy(getAddress<T>())
+  FB_DYNAMIC_APPLY(type_, FB_X);
+#undef FB_X
+  type_ = NULLT;
+  u_.nul = nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+}
diff --git a/faux-folly/folly/dynamic.h b/faux-folly/folly/dynamic.h
new file mode 100644
index 0000000..ac386ee
--- /dev/null
+++ b/faux-folly/folly/dynamic.h
@@ -0,0 +1,555 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * This is a runtime dynamically typed value.  It holds types from a
+ * specific predetermined set of types (ints, bools, arrays, etc).  In
+ * particular, it can be used as a convenient in-memory representation
+ * for complete json objects.
+ *
+ * In general you can try to use these objects as if they were the
+ * type they represent (although in some cases with a slightly less
+ * complete interface than the raw type), and it'll just throw a
+ * TypeError if it is used in an illegal way.
+ *
+ * Some examples:
+ *
+ *   dynamic twelve = 12;
+ *   dynamic str = "string";
+ *   dynamic map = dynamic::object;
+ *   map[str] = twelve;
+ *   map[str + "another_str"] = { "array", "of", 4, "elements" };
+ *   map.insert("null_element", nullptr);
+ *   ++map[str];
+ *   assert(map[str] == 13);
+ *
+ *   // Building a complex object with a sub array inline:
+ *   dynamic d = dynamic::object
+ *     ("key", "value")
+ *     ("key2", { "a", "array" })
+ *     ;
+ *
+ * Also see folly/json.h for the serialization and deserialization
+ * functions for JSON.
+ *
+ * Note: dynamic is not DefaultConstructible.  Rationale:
+ *
+ *   - The intuitive thing to initialize a defaulted dynamic to would
+ *     be nullptr.
+ *
+ *   - However, the expression dynamic d = {} is required to call the
+ *     default constructor by the standard, which is confusing
+ *     behavior for dynamic unless the default constructor creates an
+ *     empty array.
+ *
+ * Additional documentation is in folly/docs/Dynamic.md.
+ *
+ * @author Jordan DeLong <delong.j@fb.com>
+ */
+
+#ifndef FOLLY_DYNAMIC_H_
+#define FOLLY_DYNAMIC_H_
+
+#include <cstdint>
+#include <initializer_list>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <boost/operators.hpp>
+
+#include <folly/FBString.h>
+#include <folly/Range.h>
+#include <folly/Traits.h>
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+struct dynamic;
+struct TypeError;
+
+//////////////////////////////////////////////////////////////////////
+
+struct dynamic : private boost::operators<dynamic> {
+  enum Type {
+    NULLT,
+    ARRAY,
+    BOOL,
+    DOUBLE,
+    INT64,
+    OBJECT,
+    STRING,
+  };
+
+  /*
+   * We support direct iteration of arrays, and indirect iteration of objects.
+   * See begin(), end(), keys(), values(), and items() for more.
+   *
+   * Array iterators dereference as the elements in the array.
+   * Object key iterators dereference as the keys in the object.
+   * Object value iterators dereference as the values in the object.
+   * Object item iterators dereference as pairs of (key, value).
+   */
+private:
+  typedef std::vector<dynamic> Array;
+public:
+  typedef Array::const_iterator const_iterator;
+  typedef dynamic value_type;
+  struct const_key_iterator;
+  struct const_value_iterator;
+  struct const_item_iterator;
+
+  /*
+   * Creation routines for making dynamic objects.  Objects are maps
+   * from key to value (so named due to json-related origins here).
+   *
+   * Example:
+   *
+   *   // Make a fairly complex dynamic:
+   *   dynamic d = dynamic::object("key", "value1")
+   *                              ("key2", { "value", "with", 4, "words" });
+   *
+   *   // Build an object in a few steps:
+   *   dynamic d = dynamic::object;
+   *   d["key"] = 12;
+   *   d["something_else"] = { 1, 2, 3, nullptr };
+   */
+private:
+  struct ObjectMaker;
+
+public:
+  static ObjectMaker object();
+  static ObjectMaker object(dynamic&&, dynamic&&);
+  static ObjectMaker object(dynamic const&, dynamic&&);
+  static ObjectMaker object(dynamic&&, dynamic const&);
+  static ObjectMaker object(dynamic const&, dynamic const&);
+
+  /*
+   * String compatibility constructors.
+   */
+  /* implicit */ dynamic(StringPiece val);
+  /* implicit */ dynamic(char const* val);
+  /* implicit */ dynamic(std::string const& val);
+  /* implicit */ dynamic(fbstring const& val);
+  /* implicit */ dynamic(fbstring&& val);
+
+  /*
+   * This is part of the plumbing for object(), above.  Used to create
+   * a new object dynamic.
+   */
+  /* implicit */ dynamic(ObjectMaker (*)());
+  /* implicit */ dynamic(ObjectMaker const&) = delete;
+  /* implicit */ dynamic(ObjectMaker&&);
+
+  /*
+   * Create a new array from an initializer list.
+   *
+   * For example:
+   *
+   *   dynamic v = { 1, 2, 3, "foo" };
+   */
+  /* implicit */ dynamic(std::initializer_list<dynamic> il);
+
+  /*
+   * Conversion constructors from most of the other types.
+   */
+  template<class T> /* implicit */ dynamic(T t);
+
+  /*
+   * Create a dynamic that is an array of the values from the supplied
+   * iterator range.
+   */
+  template<class Iterator> dynamic(Iterator first, Iterator last);
+
+  dynamic(dynamic const&);
+  dynamic(dynamic&&) noexcept;
+  ~dynamic() noexcept;
+
+  /*
+   * "Deep" equality comparison.  This will compare all the way down
+   * an object or array, and is potentially expensive.
+   */
+  bool operator==(dynamic const& o) const;
+
+  /*
+   * For all types except object this returns the natural ordering on
+   * those types.  For objects, we throw TypeError.
+   */
+  bool operator<(dynamic const& o) const;
+
+  /*
+   * General operators.
+   *
+   * These throw TypeError when used with types or type combinations
+   * that don't support them.
+   *
+   * These functions may also throw if you use 64-bit integers with
+   * doubles when the integers are too big to fit in a double.
+   */
+  dynamic& operator+=(dynamic const&);
+  dynamic& operator-=(dynamic const&);
+  dynamic& operator*=(dynamic const&);
+  dynamic& operator/=(dynamic const&);
+  dynamic& operator%=(dynamic const&);
+  dynamic& operator|=(dynamic const&);
+  dynamic& operator&=(dynamic const&);
+  dynamic& operator^=(dynamic const&);
+  dynamic& operator++();
+  dynamic& operator--();
+
+  /*
+   * Assignment from other dynamics.  Because of the implicit conversion
+   * to dynamic from its potential types, you can use this to change the
+   * type pretty intuitively.
+   *
+   * Basic guarantee only.
+   */
+  dynamic& operator=(dynamic const&);
+  dynamic& operator=(dynamic&&) noexcept;
+
+  /*
+   * For simple dynamics (not arrays or objects), this prints the
+   * value to an std::ostream in the expected way.  Respects the
+   * formatting manipulators that have been sent to the stream
+   * already.
+   *
+   * If the dynamic holds an object or array, this prints them in a
+   * format very similar to JSON.  (It will in fact actually be JSON
+   * as long as the dynamic validly represents a JSON object---i.e. it
+   * can't have non-string keys.)
+   */
+  friend std::ostream& operator<<(std::ostream&, dynamic const&);
+
+  /*
+   * Returns true if this dynamic is of the specified type.
+   */
+  bool isString() const;
+  bool isObject() const;
+  bool isBool() const;
+  bool isNull() const;
+  bool isArray() const;
+  bool isDouble() const;
+  bool isInt() const;
+
+  /*
+   * Returns: isInt() || isDouble().
+   */
+  bool isNumber() const;
+
+  /*
+   * Returns the type of this dynamic.
+   */
+  Type type() const;
+
+  /*
+   * Returns the type of this dynamic as a printable string.
+   */
+  const char* typeName() const;
+
+  /*
+   * Extract a value while trying to convert to the specified type.
+   * Throws exceptions if we cannot convert from the real type to the
+   * requested type.
+   *
+   * Note you can only use this to access integral types or strings,
+   * since arrays and objects are generally best dealt with as a
+   * dynamic.
+   */
+  fbstring asString() const;
+  double   asDouble() const;
+  int64_t  asInt() const;
+  bool     asBool() const;
+
+  /*
+   * Extract the value stored in this dynamic without type conversion.
+   *
+   * These will throw a TypeError if the dynamic has a different type.
+   */
+  const fbstring& getString() const&;
+  double          getDouble() const&;
+  int64_t         getInt() const&;
+  bool            getBool() const&;
+  fbstring& getString() &;
+  double&   getDouble() &;
+  int64_t&  getInt() &;
+  bool&     getBool() &;
+  fbstring getString() &&;
+  double   getDouble() &&;
+  int64_t  getInt() &&;
+  bool     getBool() &&;
+
+  /*
+   * It is occasionally useful to access a string's internal pointer
+   * directly, without the type conversion of `asString()`.
+   *
+   * These will throw a TypeError if the dynamic is not a string.
+   */
+  const char* data()  const&;
+  const char* data()  && = delete;
+  const char* c_str() const&;
+  const char* c_str() && = delete;
+  StringPiece stringPiece() const;
+
+  /*
+   * Returns: true if this dynamic is null, an empty array, an empty
+   * object, or an empty string.
+   */
+  bool empty() const;
+
+  /*
+   * If this is an array or an object, returns the number of elements
+   * contained.  If it is a string, returns the length.  Otherwise
+   * throws TypeError.
+   */
+  std::size_t size() const;
+
+  /*
+   * You can iterate over the values of the array.  Calling these on
+   * non-arrays will throw a TypeError.
+   */
+  const_iterator begin()  const;
+  const_iterator end()    const;
+
+private:
+  /*
+   * Helper object returned by keys(), values(), and items().
+   */
+  template <class T> struct IterableProxy;
+
+public:
+  /*
+   * You can iterate over the keys, values, or items (std::pair of key and
+   * value) in an object.  Calling these on non-objects will throw a TypeError.
+   */
+  IterableProxy<const_key_iterator> keys() const;
+  IterableProxy<const_value_iterator> values() const;
+  IterableProxy<const_item_iterator> items() const;
+
+  /*
+   * AssociativeContainer-style find interface for objects.  Throws if
+   * this is not an object.
+   *
+   * Returns: items().end() if the key is not present, or a
+   * const_item_iterator pointing to the item.
+   */
+  const_item_iterator find(dynamic const&) const;
+
+  /*
+   * If this is an object, returns whether it contains a field with
+   * the given name.  Otherwise throws TypeError.
+   */
+  std::size_t count(dynamic const&) const;
+
+  /*
+   * For objects or arrays, provides access to sub-fields by index or
+   * field name.
+   *
+   * Using these with dynamic objects that are not arrays or objects
+   * will throw a TypeError.  Using an index that is out of range or
+   * object-element that's not present throws std::out_of_range.
+   */
+  dynamic const& at(dynamic const&) const&;
+  dynamic&       at(dynamic const&) &;
+  dynamic        at(dynamic const&) &&;
+
+  /*
+   * Like 'at', above, except it returns either a pointer to the contained
+   * object or nullptr if it wasn't found. This allows a key to be tested for
+   * containment and retrieved in one operation. Example:
+   *
+   *   if (auto* found = d.get_ptr(key))
+   *     // use *found;
+   *
+   * Using these with dynamic objects that are not arrays or objects
+   * will throw a TypeError.
+   */
+  const dynamic* get_ptr(dynamic const&) const&;
+  dynamic* get_ptr(dynamic const&) &;
+  dynamic* get_ptr(dynamic const&) && = delete;
+
+  /*
+   * This works for access to both objects and arrays.
+   *
+   * In the case of an array, the index must be an integer, and this will throw
+   * std::out_of_range if it is less than zero or greater than size().
+   *
+   * In the case of an object, the non-const overload inserts a null
+   * value if the key isn't present.  The const overload will throw
+   * std::out_of_range if the key is not present.
+   *
+   * These functions do not invalidate iterators.
+   */
+  dynamic&       operator[](dynamic const&) &;
+  dynamic const& operator[](dynamic const&) const&;
+  dynamic        operator[](dynamic const&) &&;
+
+  /*
+   * Only defined for objects, throws TypeError otherwise.
+   *
+   * getDefault will return the value associated with the supplied key, the
+   * supplied default otherwise. setDefault will set the key to the supplied
+   * default if it is not yet set, otherwise leaving it. setDefault returns
+   * a reference to the existing value if present, the new value otherwise.
+   */
+  dynamic
+  getDefault(const dynamic& k, const dynamic& v = dynamic::object) const&;
+  dynamic getDefault(const dynamic& k, dynamic&& v) const&;
+  dynamic getDefault(const dynamic& k, const dynamic& v = dynamic::object) &&;
+  dynamic getDefault(const dynamic& k, dynamic&& v) &&;
+  template<class K, class V = dynamic>
+  dynamic& setDefault(K&& k, V&& v = dynamic::object);
+
+  /*
+   * Resizes an array so it has at n elements, using the supplied
+   * default to fill new elements.  Throws TypeError if this dynamic
+   * is not an array.
+   *
+   * May invalidate iterators.
+   *
+   * Post: size() == n
+   */
+  void resize(std::size_t n, dynamic const& = nullptr);
+
+  /*
+   * Inserts the supplied key-value pair to an object, or throws if
+   * it's not an object.
+   *
+   * Invalidates iterators.
+   */
+  template<class K, class V> void insert(K&&, V&& val);
+
+  /*
+   * Erase an element from a dynamic object, by key.
+   *
+   * Invalidates iterators to the element being erased.
+   *
+   * Returns the number of elements erased (i.e. 1 or 0).
+   */
+  std::size_t erase(dynamic const& key);
+
+  /*
+   * Erase an element from a dynamic object or array, using an
+   * iterator or an iterator range.
+   *
+   * In arrays, invalidates iterators to elements after the element
+   * being erased.  In objects, invalidates iterators to the elements
+   * being erased.
+   *
+   * Returns a new iterator to the first element beyond any elements
+   * removed, or end() if there are none.  (The iteration order does
+   * not change.)
+   */
+  const_iterator erase(const_iterator it);
+  const_iterator erase(const_iterator first, const_iterator last);
+
+  const_key_iterator erase(const_key_iterator it);
+  const_key_iterator erase(const_key_iterator first, const_key_iterator last);
+
+  const_value_iterator erase(const_value_iterator it);
+  const_value_iterator erase(const_value_iterator first,
+                             const_value_iterator last);
+
+  const_item_iterator erase(const_item_iterator it);
+  const_item_iterator erase(const_item_iterator first,
+                            const_item_iterator last);
+  /*
+   * Append elements to an array.  If this is not an array, throws
+   * TypeError.
+   *
+   * Invalidates iterators.
+   */
+  void push_back(dynamic const&);
+  void push_back(dynamic&&);
+
+  /*
+   * Remove an element from the back of an array.  If this is not an array,
+   * throws TypeError.
+   *
+   * Does not invalidate iterators.
+   */
+  void pop_back();
+
+  /*
+   * Get a hash code.  This function is called by a std::hash<>
+   * specialization, also.
+   *
+   * Throws TypeError if this is an object, array, or null.
+   */
+  std::size_t hash() const;
+
+private:
+  friend struct TypeError;
+  struct ObjectImpl;
+  template<class T> struct TypeInfo;
+  template<class T> struct CompareOp;
+  template<class T> struct GetAddrImpl;
+  template<class T> struct PrintImpl;
+
+  template<class T> T const& get() const;
+  template<class T> T&       get();
+  template<class T> T*       get_nothrow() & noexcept;
+  template<class T> T const* get_nothrow() const& noexcept;
+  template<class T> T*       get_nothrow() && noexcept = delete;
+  template<class T> T*       getAddress() noexcept;
+  template<class T> T const* getAddress() const noexcept;
+
+  template<class T> T asImpl() const;
+
+  static char const* typeName(Type);
+  void destroy() noexcept;
+  void print(std::ostream&) const;
+
+private:
+  Type type_;
+  union Data {
+    explicit Data() : nul(nullptr) {}
+    ~Data() {}
+
+    // XXX: gcc does an ICE if we use std::nullptr_t instead of void*
+    // here.  See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50361
+    void* nul;
+    Array array;
+    bool boolean;
+    double doubl;
+    int64_t integer;
+    fbstring string;
+
+    /*
+     * Objects are placement new'd here.  We have to use a char buffer
+     * because we don't know the type here (std::unordered_map<> with
+     * dynamic would be parameterizing a std:: template with an
+     * incomplete type right now).  (Note that in contrast we know it
+     * is ok to do this with fbvector because we own it.)
+     */
+    std::aligned_storage<
+      sizeof(std::unordered_map<int,int>),
+      alignof(std::unordered_map<int,int>)
+    >::type objectBuffer;
+  } u_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+}
+
+#include <folly/dynamic-inl.h>
+
+#endif
diff --git a/faux-folly/folly/futures/Barrier.cpp b/faux-folly/folly/futures/Barrier.cpp
new file mode 100644
index 0000000..e9f5ee8
--- /dev/null
+++ b/faux-folly/folly/futures/Barrier.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 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/futures/Barrier.h>
+
+namespace folly { namespace futures {
+
+Barrier::Barrier(uint32_t n)
+  : size_(n),
+    controlBlock_(allocateControlBlock()) { }
+
+Barrier::~Barrier() {
+  auto block = controlBlock_.load(std::memory_order_relaxed);
+  auto prev = block->valueAndReaderCount.load(std::memory_order_relaxed);
+  DCHECK_EQ(prev >> kReaderShift, 0);
+  auto val = prev & kValueMask;
+  auto p = promises(block);
+
+  for (uint32_t i = 0; i < val; ++i) {
+    p[i].setException(
+        folly::make_exception_wrapper<std::runtime_error>("Barrier destroyed"));
+  }
+
+  freeControlBlock(controlBlock_);
+}
+
+auto Barrier::allocateControlBlock() -> ControlBlock* {
+  auto block = static_cast<ControlBlock*>(malloc(controlBlockSize(size_)));
+  if (!block) {
+    throw std::bad_alloc();
+  }
+  block->valueAndReaderCount = 0;
+
+  auto p = promises(block);
+  uint32_t i = 0;
+  try {
+    for (i = 0; i < size_; ++i) {
+      new (p + i) BoolPromise();
+    }
+  } catch (...) {
+    for (; i != 0; --i) {
+      p[i - 1].~BoolPromise();
+    }
+    throw;
+  }
+
+  return block;
+}
+
+void Barrier::freeControlBlock(ControlBlock* block) {
+  auto p = promises(block);
+  for (uint32_t i = size_; i != 0; --i) {
+    p[i - 1].~BoolPromise();
+  }
+  free(block);
+}
+
+folly::Future<bool> Barrier::wait() {
+  // Load the current control block first. As we know there is at least
+  // one thread in the current epoch (us), this means that the value is
+  // < size_, so controlBlock_ can't change until we bump the value below.
+  auto block = controlBlock_.load(std::memory_order_acquire);
+  auto p = promises(block);
+
+  // Bump the value and record ourselves as reader.
+  // This ensures that block stays allocated, as the reader count is > 0.
+  auto prev = block->valueAndReaderCount.fetch_add(kReader + 1,
+                                                   std::memory_order_acquire);
+
+  auto prevValue = static_cast<uint32_t>(prev & kValueMask);
+  DCHECK_LT(prevValue, size_);
+  auto future = p[prevValue].getFuture();
+
+  if (prevValue + 1 == size_) {
+    // Need to reset the barrier before fulfilling any futures. This is
+    // when the epoch is flipped to the next.
+    controlBlock_.store(allocateControlBlock(), std::memory_order_release);
+
+    p[0].setValue(true);
+    for (uint32_t i = 1; i < size_; ++i) {
+      p[i].setValue(false);
+    }
+  }
+
+  // Free the control block if we're the last reader at max value.
+  prev = block->valueAndReaderCount.fetch_sub(kReader,
+                                              std::memory_order_acq_rel);
+  if (prev == (kReader | uint64_t(size_))) {
+    freeControlBlock(block);
+  }
+
+  return future;
+}
+
+}}  // namespaces
diff --git a/faux-folly/folly/futures/Barrier.h b/faux-folly/folly/futures/Barrier.h
new file mode 100644
index 0000000..64746de
--- /dev/null
+++ b/faux-folly/folly/futures/Barrier.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <cstdint>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+
+namespace folly { namespace futures {
+
+// A folly::Future-istic Barrier synchronization primitive
+//
+// The barrier is initialized with a count N.
+//
+// The first N-1 calls to wait() return uncompleted futures.
+//
+// The Nth call to wait() completes the previous N-1 futures successfully,
+// returns a future that is already completed successfully, and resets the
+// barrier; the barrier may be reused immediately, as soon as at least one
+// of the future completions has been observed.
+//
+// Of these N futures, exactly one is completed with true, while the others are
+// completed with false; it is unspecified which future completes with true.
+// (This may be used to elect a "leader" among a group of threads.)
+//
+// If the barrier is destroyed, any futures already returned by wait() will
+// complete with an error.
+class Barrier {
+ public:
+  explicit Barrier(uint32_t n);
+  ~Barrier();
+
+  folly::Future<bool> wait();
+
+ private:
+  typedef folly::Promise<bool> BoolPromise;
+
+  static constexpr uint64_t kReaderShift = 32;
+  static constexpr uint64_t kReader = uint64_t(1) << kReaderShift;
+  static constexpr uint64_t kValueMask = kReader - 1;
+
+  // For each "epoch" that the barrier is active, we have a different
+  // ControlBlock. The ControlBlock contains the current barrier value
+  // and the number of readers (currently inside wait()) packed into a
+  // 64-bit value.
+  //
+  // The ControlBlock is allocated as long as either:
+  // - there are threads currently inside wait() (reader count > 0), or
+  // - the value has not yet reached size_ (value < size_)
+  //
+  // The array of size_ Promise objects is allocated immediately following
+  // valueAndReaderCount.
+
+  struct ControlBlock {
+    // Reader count in most significant 32 bits
+    // Value in least significant 32 bits
+    std::atomic<uint64_t> valueAndReaderCount;
+  };
+
+  struct ControlBlockAndPromise {
+    ControlBlock cb;
+    BoolPromise promises[1];
+  };
+
+  static BoolPromise* promises(ControlBlock* cb) {
+    return reinterpret_cast<ControlBlockAndPromise*>(cb)->promises;
+  }
+
+  static size_t controlBlockSize(size_t n) {
+    return offsetof(ControlBlockAndPromise, promises) + n * sizeof(BoolPromise);
+  }
+
+  ControlBlock* allocateControlBlock();
+  void freeControlBlock(ControlBlock* b);
+
+  uint32_t size_;
+  std::atomic<ControlBlock*> controlBlock_;
+};
+
+}}  // namespaces
diff --git a/faux-folly/folly/futures/DrivableExecutor.h b/faux-folly/folly/futures/DrivableExecutor.h
new file mode 100644
index 0000000..57f177c
--- /dev/null
+++ b/faux-folly/folly/futures/DrivableExecutor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 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/Executor.h>
+
+namespace folly {
+
+/*
+ * A DrivableExecutor can be driven via its drive() method
+ * Examples include EventBase (via loopOnce()) and ManualExecutor
+ * (via makeProgress()).
+ *
+ * This interface is most handy in conjunction with
+ * Future<T>::getVia(DrivableExecutor*) and
+ * Future<T>::waitVia(DrivableExecutor*)
+ *
+ * These call drive() * repeatedly until the Future is fulfilled.
+ * getVia() returns the value (or throws the exception) and waitVia() returns
+ * the same Future for chainability.
+ *
+ * These will be most helpful in tests, for instance if you need to pump a mock
+ * EventBase until Futures complete.
+ */
+class DrivableExecutor : public virtual Executor {
+ public:
+  virtual ~DrivableExecutor() = default;
+
+  // Make progress on this Executor's work.
+  virtual void drive() = 0;
+};
+
+} // folly
diff --git a/faux-folly/folly/futures/Future-inl.h b/faux-folly/folly/futures/Future-inl.h
new file mode 100644
index 0000000..aae3658
--- /dev/null
+++ b/faux-folly/folly/futures/Future-inl.h
@@ -0,0 +1,1446 @@
+/*
+ * Copyright 2015 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 <chrono>
+#include <random>
+#include <thread>
+
+#include <folly/ExecutorWithCancellation.h>
+
+#include <folly/Baton.h>
+#include <folly/Optional.h>
+#include <folly/Random.h>
+#include <folly/Traits.h>
+#include <folly/futures/detail/Core.h>
+#include <folly/futures/Timekeeper.h>
+
+namespace folly {
+
+class Timekeeper;
+
+namespace detail {
+  Timekeeper* getTimekeeperSingleton();
+}
+
+template <class T>
+Future<T>::Future(Future<T>&& other) noexcept : core_(other.core_) {
+  other.core_ = nullptr;
+}
+
+template <class T>
+Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
+  std::swap(core_, other.core_);
+  return *this;
+}
+
+template <class T>
+template <class T2, typename>
+Future<T>::Future(T2&& val)
+  : core_(new detail::Core<T>(Try<T>(std::forward<T2>(val)))) {}
+
+template <class T>
+template <typename, typename>
+Future<T>::Future()
+  : core_(new detail::Core<T>(Try<T>(T()))) {}
+
+template <class T>
+Future<T>::~Future() {
+  detach();
+}
+
+template <class T>
+void Future<T>::detach() {
+  if (core_) {
+    core_->detachFuture();
+    core_ = nullptr;
+  }
+}
+
+template <class T>
+void Future<T>::throwIfInvalid() const {
+  if (!core_)
+    throw NoState();
+}
+
+template <class T>
+template <class F>
+void Future<T>::setCallback_(F&& func) {
+  throwIfInvalid();
+  core_->setCallback(std::move(func));
+}
+
+// unwrap
+
+template <class T>
+template <class F>
+typename std::enable_if<isFuture<F>::value,
+                        Future<typename isFuture<T>::Inner>>::type
+Future<T>::unwrap() {
+  return then([](Future<typename isFuture<T>::Inner> internal_future) {
+      return internal_future;
+  });
+}
+
+// then
+
+// Variant: returns a value
+// e.g. f.then([](Try<T>&& t){ return t.value(); });
+template <class T>
+template <typename F, typename R, bool isTry, typename... Args>
+typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
+Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
+  static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
+  typedef typename R::ReturnsFuture::Inner B;
+
+  throwIfInvalid();
+
+  // wrap these so we can move them into the lambda
+  folly::MoveWrapper<Promise<B>> p;
+  p->core_->setInterruptHandlerNoLock(core_->getInterruptHandler());
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+
+  // grab the Future now before we lose our handle on the Promise
+  auto f = p->getFuture();
+  f.core_->setExecutorNoLock(getExecutor());
+
+  /* This is a bit tricky.
+
+     We can't just close over *this in case this Future gets moved. So we
+     make a new dummy Future. We could figure out something more
+     sophisticated that avoids making a new Future object when it can, as an
+     optimization. But this is correct.
+
+     core_ can't be moved, it is explicitly disallowed (as is copying). But
+     if there's ever a reason to allow it, this is one place that makes that
+     assumption and would need to be fixed. We use a standard shared pointer
+     for core_ (by copying it in), which means in essence obj holds a shared
+     pointer to itself.  But this shouldn't leak because Promise will not
+     outlive the continuation, because Promise will setException() with a
+     broken Promise if it is destructed before completed. We could use a
+     weak pointer but it would have to be converted to a shared pointer when
+     func is executed (because the Future returned by func may possibly
+     persist beyond the callback, if it gets moved), and so it is an
+     optimization to just make it shared from the get-go.
+
+     We have to move in the Promise and func using the MoveWrapper
+     hack. (func could be copied but it's a big drag on perf).
+
+     Two subtle but important points about this design. detail::Core has no
+     back pointers to Future or Promise, so if Future or Promise get moved
+     (and they will be moved in performant code) we don't have to do
+     anything fancy. And because we store the continuation in the
+     detail::Core, not in the Future, we can execute the continuation even
+     after the Future has gone out of scope. This is an intentional design
+     decision. It is likely we will want to be able to cancel a continuation
+     in some circumstances, but I think it should be explicit not implicit
+     in the destruction of the Future used to create it.
+     */
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      if (!isTry && t.hasException()) {
+        p->setException(std::move(t.exception()));
+      } else {
+        p->setWith([&]() {
+          return (*funcm)(t.template get<isTry, Args>()...);
+        });
+      }
+    });
+
+  return f;
+}
+
+// Variant: returns a Future
+// e.g. f.then([](T&& t){ return makeFuture<T>(t); });
+template <class T>
+template <typename F, typename R, bool isTry, typename... Args>
+typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
+Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
+  static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
+  typedef typename R::ReturnsFuture::Inner B;
+
+  throwIfInvalid();
+
+  // wrap these so we can move them into the lambda
+  folly::MoveWrapper<Promise<B>> p;
+  p->core_->setInterruptHandlerNoLock(core_->getInterruptHandler());
+  folly::MoveWrapper<F> funcm(std::forward<F>(func));
+
+  // grab the Future now before we lose our handle on the Promise
+  auto f = p->getFuture();
+  f.core_->setExecutorNoLock(getExecutor());
+
+  setCallback_(
+    [p, funcm](Try<T>&& t) mutable {
+      if (!isTry && t.hasException()) {
+        p->setException(std::move(t.exception()));
+      } else {
+        try {
+          auto f2 = (*funcm)(t.template get<isTry, Args>()...);
+          // that didn't throw, now we can steal p
+          f2.setCallback_([p](Try<B>&& b) mutable {
+            p->setTry(std::move(b));
+          });
+        } catch (const std::exception& e) {
+          p->setException(exception_wrapper(std::current_exception(), e));
+        } catch (...) {
+          p->setException(exception_wrapper(std::current_exception()));
+        }
+      }
+    });
+
+  return f;
+}
+
+template <typename T>
+template <typename R, typename Caller, typename... Args>
+  Future<typename isFuture<R>::Inner>
+Future<T>::then(R(Caller::*func)(Args...), Caller *instance) {
+  typedef typename std::remove_cv<
+    typename std::remove_reference<
+      typename detail::ArgType<Args...>::FirstArg>::type>::type FirstArg;
+  return then([instance, func](Try<T>&& t){
+    return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
+  });
+}
+
+template <class T>
+template <class Executor, class Arg, class... Args>
+auto Future<T>::then(Executor* x, Arg&& arg, Args&&... args)
+  -> typename std::enable_if<!has_cancellation<Executor>::value,
+                             decltype(this->then(std::forward<Arg>(arg),
+                                                 std::forward<Args>(args)...))>::type
+{
+  auto oldX = getExecutor();
+  setExecutor(x);
+  return this->then(std::forward<Arg>(arg), std::forward<Args>(args)...).
+               via(oldX);
+}
+
+template <class T>
+template <class Executor, class Arg, class... Args>
+auto Future<T>::then(Executor* x, Arg&& arg, Args&&... args)
+  -> typename std::enable_if<has_cancellation<Executor>::value,
+                             decltype(this->then(std::forward<Arg>(arg),
+                                                 std::forward<Args>(args)...))>::type
+{
+  return this->then(x, x->getCancellation(), std::forward<Arg>(arg), std::forward<Args>(args)...);
+}
+
+template <class T>
+template <class Executor, class Arg, class... Args>
+auto Future<T>::then(Executor* x, const Cancellation &cx, Arg&& arg, Args&&... args)
+  -> decltype(this->then(std::forward<Arg>(arg),
+                         std::forward<Args>(args)...))
+{
+  auto oldX = getExecutor();
+  setExecutor(x);
+  setCancellation(cx);
+  return this->then(std::forward<Arg>(arg), std::forward<Args>(args)...).
+               via(oldX);
+}
+
+template <class T>
+Future<Unit> Future<T>::then() {
+  return then([] () {});
+}
+
+// onError where the callback returns T
+template <class T>
+template <class F>
+typename std::enable_if<
+  !detail::callableWith<F, exception_wrapper>::value &&
+  !detail::Extract<F>::ReturnsFuture::value,
+  Future<T>>::type
+Future<T>::onError(F&& func) {
+  typedef typename detail::Extract<F>::FirstArg Exn;
+  static_assert(
+      std::is_same<typename detail::Extract<F>::RawReturn, T>::value,
+      "Return type of onError callback must be T or Future<T>");
+
+  Promise<T> p;
+  p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler());
+  auto f = p.getFuture();
+  auto pm = folly::makeMoveWrapper(std::move(p));
+  auto funcm = folly::makeMoveWrapper(std::move(func));
+  setCallback_([pm, funcm](Try<T>&& t) mutable {
+    if (!t.template withException<Exn>([&] (Exn& e) {
+          pm->setWith([&]{
+            return (*funcm)(e);
+          });
+        })) {
+      pm->setTry(std::move(t));
+    }
+  });
+
+  return f;
+}
+
+// onError where the callback returns Future<T>
+template <class T>
+template <class F>
+typename std::enable_if<
+  !detail::callableWith<F, exception_wrapper>::value &&
+  detail::Extract<F>::ReturnsFuture::value,
+  Future<T>>::type
+Future<T>::onError(F&& func) {
+  static_assert(
+      std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
+      "Return type of onError callback must be T or Future<T>");
+  typedef typename detail::Extract<F>::FirstArg Exn;
+
+  Promise<T> p;
+  auto f = p.getFuture();
+  auto pm = folly::makeMoveWrapper(std::move(p));
+  auto funcm = folly::makeMoveWrapper(std::move(func));
+  setCallback_([pm, funcm](Try<T>&& t) mutable {
+    if (!t.template withException<Exn>([&] (Exn& e) {
+          try {
+            auto f2 = (*funcm)(e);
+            f2.setCallback_([pm](Try<T>&& t2) mutable {
+              pm->setTry(std::move(t2));
+            });
+          } catch (const std::exception& e2) {
+            pm->setException(exception_wrapper(std::current_exception(), e2));
+          } catch (...) {
+            pm->setException(exception_wrapper(std::current_exception()));
+          }
+        })) {
+      pm->setTry(std::move(t));
+    }
+  });
+
+  return f;
+}
+
+template <class T>
+template <class F>
+Future<T> Future<T>::ensure(F func) {
+  MoveWrapper<F> funcw(std::move(func));
+  return this->then([funcw](Try<T>&& t) mutable {
+    (*funcw)();
+    return makeFuture(std::move(t));
+  });
+}
+
+template <class T>
+template <class F>
+Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) {
+  auto funcw = folly::makeMoveWrapper(std::forward<F>(func));
+  return within(dur, tk)
+    .onError([funcw](TimedOut const&) { return (*funcw)(); });
+}
+
+template <class T>
+template <class F>
+typename std::enable_if<
+  detail::callableWith<F, exception_wrapper>::value &&
+  detail::Extract<F>::ReturnsFuture::value,
+  Future<T>>::type
+Future<T>::onError(F&& func) {
+  static_assert(
+      std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
+      "Return type of onError callback must be T or Future<T>");
+
+  Promise<T> p;
+  auto f = p.getFuture();
+  auto pm = folly::makeMoveWrapper(std::move(p));
+  auto funcm = folly::makeMoveWrapper(std::move(func));
+  setCallback_([pm, funcm](Try<T> t) mutable {
+    if (t.hasException()) {
+      try {
+        auto f2 = (*funcm)(std::move(t.exception()));
+        f2.setCallback_([pm](Try<T> t2) mutable {
+          pm->setTry(std::move(t2));
+        });
+      } catch (const std::exception& e2) {
+        pm->setException(exception_wrapper(std::current_exception(), e2));
+      } catch (...) {
+        pm->setException(exception_wrapper(std::current_exception()));
+      }
+    } else {
+      pm->setTry(std::move(t));
+    }
+  });
+
+  return f;
+}
+
+// onError(exception_wrapper) that returns T
+template <class T>
+template <class F>
+typename std::enable_if<
+  detail::callableWith<F, exception_wrapper>::value &&
+  !detail::Extract<F>::ReturnsFuture::value,
+  Future<T>>::type
+Future<T>::onError(F&& func) {
+  static_assert(
+      std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
+      "Return type of onError callback must be T or Future<T>");
+
+  Promise<T> p;
+  auto f = p.getFuture();
+  auto pm = folly::makeMoveWrapper(std::move(p));
+  auto funcm = folly::makeMoveWrapper(std::move(func));
+  setCallback_([pm, funcm](Try<T> t) mutable {
+    if (t.hasException()) {
+      pm->setWith([&]{
+        return (*funcm)(std::move(t.exception()));
+      });
+    } else {
+      pm->setTry(std::move(t));
+    }
+  });
+
+  return f;
+}
+
+template <class T>
+void Future<T>::onErrorThrowIn(Executor* x)
+{
+    auto cx = core_->getCancellation();
+    onError([x, cx] (exception_wrapper ew) {
+            auto h = cx.hold_state();
+            if (!h) return makeFuture<T>(ew);
+
+            x->add([ew] { ew.throwException(); });
+            return makeFuture<T>(std::logic_error("The real exception was passed to an Executor."));
+        });
+}
+
+template <class T>
+typename std::add_lvalue_reference<T>::type Future<T>::value() {
+  throwIfInvalid();
+
+  return core_->getTry().value();
+}
+
+template <class T>
+typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
+  throwIfInvalid();
+
+  return core_->getTry().value();
+}
+
+template <class T>
+Try<T>& Future<T>::getTry() {
+  throwIfInvalid();
+
+  return core_->getTry();
+}
+
+template <class T>
+Optional<Try<T>> Future<T>::poll() {
+  Optional<Try<T>> o;
+  if (core_->ready()) {
+    o = std::move(core_->getTry());
+  }
+  return o;
+}
+
+template <class T>
+inline Future<T> Future<T>::via(Executor* executor, int8_t priority) && {
+  throwIfInvalid();
+
+  setExecutor(executor, priority);
+
+  return std::move(*this);
+}
+
+template <class T>
+inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
+  throwIfInvalid();
+
+  MoveWrapper<Promise<T>> p;
+  auto f = p->getFuture();
+  then([p](Try<T>&& t) mutable { p->setTry(std::move(t)); });
+  return std::move(f).via(executor, priority);
+}
+
+template <class T>
+inline Future<T> Future<T>::via(Executor* executor, const Cancellation& cx, int8_t priority) && {
+  throwIfInvalid();
+
+  setExecutor(executor, priority);
+  setCancellation(cx);
+
+  return std::move(*this);
+}
+
+template <class T>
+inline Future<T> Future<T>::via(Executor* executor, const Cancellation& cx, int8_t priority) & {
+  throwIfInvalid();
+
+  MoveWrapper<Promise<T>> p;
+  auto f = p->getFuture();
+  then([p](Try<T>&& t) mutable { p->setTry(std::move(t)); });
+  return std::move(f).via(executor, cx, priority);
+}
+
+template <class T>
+inline Future<T> Future<T>::via(ExecutorWithCancellation* executor, int8_t priority) && {
+  return via(executor, executor->getCancellation(), priority);
+}
+
+template <class T>
+inline Future<T> Future<T>::via(ExecutorWithCancellation* executor, int8_t priority) & {
+  return via(executor, executor->getCancellation(), priority);
+}
+
+template <class Func>
+auto via(Executor* x, Func func)
+  -> Future<typename isFuture<decltype(func())>::Inner>
+{
+  // TODO make this actually more performant. :-P #7260175
+  return via(x).then(func);
+}
+
+template <class T>
+bool Future<T>::isReady() const {
+  throwIfInvalid();
+  return core_->ready();
+}
+
+template <class T>
+bool Future<T>::hasValue() {
+  return getTry().hasValue();
+}
+
+template <class T>
+bool Future<T>::hasException() {
+  return getTry().hasException();
+}
+
+template <class T>
+void Future<T>::raise(exception_wrapper exception) {
+  core_->raise(std::move(exception));
+}
+
+// makeFuture
+
+template <class T>
+Future<typename std::decay<T>::type> makeFuture(T&& t) {
+  return makeFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
+}
+
+inline // for multiple translation units
+Future<Unit> makeFuture() {
+  return makeFuture(Unit{});
+}
+
+template <class F>
+auto makeFutureWith(F&& func)
+    -> Future<typename Unit::Lift<decltype(func())>::type> {
+  using LiftedResult = typename Unit::Lift<decltype(func())>::type;
+  return makeFuture<LiftedResult>(makeTryWith([&func]() mutable {
+    return func();
+  }));
+}
+
+template <class T>
+Future<T> makeFuture(std::exception_ptr const& e) {
+  return makeFuture(Try<T>(e));
+}
+
+template <class T>
+Future<T> makeFuture(exception_wrapper ew) {
+  return makeFuture(Try<T>(std::move(ew)));
+}
+
+template <class T, class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value,
+                        Future<T>>::type
+makeFuture(E const& e) {
+  return makeFuture(Try<T>(make_exception_wrapper<E>(e)));
+}
+
+template <class T>
+Future<T> makeFuture(Try<T>&& t) {
+  return Future<T>(new detail::Core<T>(std::move(t)));
+}
+
+// via
+Future<Unit> via(Executor* executor, int8_t priority) {
+  return makeFuture().via(executor, priority);
+}
+
+// mapSetCallback calls func(i, Try<T>) when every future completes
+
+template <class T, class InputIterator, class F>
+void mapSetCallback(InputIterator first, InputIterator last, F func) {
+  for (size_t i = 0; first != last; ++first, ++i) {
+    first->setCallback_([func, i](Try<T>&& t) {
+      func(i, std::move(t));
+    });
+  }
+}
+
+// collectAll (variadic)
+
+template <typename... Fs>
+typename detail::CollectAllVariadicContext<
+  typename std::decay<Fs>::type::value_type...>::type
+collectAll(Fs&&... fs) {
+  auto ctx = std::make_shared<detail::CollectAllVariadicContext<
+    typename std::decay<Fs>::type::value_type...>>();
+  detail::collectVariadicHelper<detail::CollectAllVariadicContext>(
+    ctx, std::forward<typename std::decay<Fs>::type>(fs)...);
+  return ctx->p.getFuture();
+}
+
+// collectAll (iterator)
+
+template <class InputIterator>
+Future<
+  std::vector<
+  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
+collectAll(InputIterator first, InputIterator last) {
+  typedef
+    typename std::iterator_traits<InputIterator>::value_type::value_type T;
+
+  struct CollectAllContext {
+    CollectAllContext(int n) : results(n) {}
+    ~CollectAllContext() {
+      p.setValue(std::move(results));
+    }
+    Promise<std::vector<Try<T>>> p;
+    std::vector<Try<T>> results;
+  };
+
+  auto ctx = std::make_shared<CollectAllContext>(std::distance(first, last));
+  mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
+    ctx->results[i] = std::move(t);
+  });
+  return ctx->p.getFuture();
+}
+
+// collect (iterator)
+
+namespace detail {
+
+template <typename T>
+struct CollectContext {
+  struct Nothing { explicit Nothing(int n) {} };
+
+  using Result = typename std::conditional<
+    std::is_void<T>::value,
+    void,
+    std::vector<T>>::type;
+
+  using InternalResult = typename std::conditional<
+    std::is_void<T>::value,
+    Nothing,
+    std::vector<Optional<T>>>::type;
+
+  explicit CollectContext(int n) : result(n) {}
+  ~CollectContext() {
+    if (!threw.exchange(true)) {
+      // map Optional<T> -> T
+      std::vector<T> finalResult;
+      finalResult.reserve(result.size());
+      std::transform(result.begin(), result.end(),
+                     std::back_inserter(finalResult),
+                     [](Optional<T>& o) { return std::move(o.value()); });
+      p.setValue(std::move(finalResult));
+    }
+  }
+  inline void setPartialResult(size_t i, Try<T>& t) {
+    result[i] = std::move(t.value());
+  }
+  Promise<Result> p;
+  InternalResult result;
+  std::atomic<bool> threw {false};
+};
+
+}
+
+template <class InputIterator>
+Future<typename detail::CollectContext<
+  typename std::iterator_traits<InputIterator>::value_type::value_type>::Result>
+collect(InputIterator first, InputIterator last) {
+  typedef
+    typename std::iterator_traits<InputIterator>::value_type::value_type T;
+
+  auto ctx = std::make_shared<detail::CollectContext<T>>(
+    std::distance(first, last));
+  mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
+    if (t.hasException()) {
+       if (!ctx->threw.exchange(true)) {
+         ctx->p.setException(std::move(t.exception()));
+       }
+     } else if (!ctx->threw) {
+       ctx->setPartialResult(i, t);
+     }
+  });
+  return ctx->p.getFuture();
+}
+
+// collect (variadic)
+
+template <typename... Fs>
+typename detail::CollectVariadicContext<
+  typename std::decay<Fs>::type::value_type...>::type
+collect(Fs&&... fs) {
+  auto ctx = std::make_shared<detail::CollectVariadicContext<
+    typename std::decay<Fs>::type::value_type...>>();
+  detail::collectVariadicHelper<detail::CollectVariadicContext>(
+    ctx, std::forward<typename std::decay<Fs>::type>(fs)...);
+  return ctx->p.getFuture();
+}
+
+// collectAny (iterator)
+
+template <class InputIterator>
+Future<
+  std::pair<size_t,
+            Try<
+              typename
+              std::iterator_traits<InputIterator>::value_type::value_type>>>
+collectAny(InputIterator first, InputIterator last) {
+  typedef
+    typename std::iterator_traits<InputIterator>::value_type::value_type T;
+
+  struct CollectAnyContext {
+    CollectAnyContext() {};
+    Promise<std::pair<size_t, Try<T>>> p;
+    std::atomic<bool> done {false};
+  };
+
+  auto ctx = std::make_shared<CollectAnyContext>();
+  mapSetCallback<T>(first, last, [ctx](size_t i, Try<T>&& t) {
+    if (!ctx->done.exchange(true)) {
+      ctx->p.setValue(std::make_pair(i, std::move(t)));
+    }
+  });
+  return ctx->p.getFuture();
+}
+
+// collectN (iterator)
+
+template <class InputIterator>
+Future<std::vector<std::pair<size_t, Try<typename
+  std::iterator_traits<InputIterator>::value_type::value_type>>>>
+collectN(InputIterator first, InputIterator last, size_t n) {
+  typedef typename
+    std::iterator_traits<InputIterator>::value_type::value_type T;
+  typedef std::vector<std::pair<size_t, Try<T>>> V;
+
+  struct CollectNContext {
+    V v;
+    std::atomic<size_t> completed = {0};
+    Promise<V> p;
+  };
+  auto ctx = std::make_shared<CollectNContext>();
+
+  if (size_t(std::distance(first, last)) < n) {
+    ctx->p.setException(std::runtime_error("Not enough futures"));
+  } else {
+    // for each completed Future, increase count and add to vector, until we
+    // have n completed futures at which point we fulfil our Promise with the
+    // vector
+    mapSetCallback<T>(first, last, [ctx, n](size_t i, Try<T>&& t) {
+      auto c = ++ctx->completed;
+      if (c <= n) {
+        assert(ctx->v.size() < n);
+        ctx->v.emplace_back(i, std::move(t));
+        if (c == n) {
+          ctx->p.setTry(Try<V>(std::move(ctx->v)));
+        }
+      }
+    });
+  }
+
+  return ctx->p.getFuture();
+}
+
+// reduce (iterator)
+
+template <class It, class T, class F>
+Future<T> reduce(It first, It last, T&& initial, F&& func) {
+  if (first == last) {
+    return makeFuture(std::move(initial));
+  }
+
+  typedef typename std::iterator_traits<It>::value_type::value_type ItT;
+  typedef typename std::conditional<
+    detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type Arg;
+  typedef isTry<Arg> IsTry;
+
+  folly::MoveWrapper<T> minitial(std::move(initial));
+  auto sfunc = std::make_shared<F>(std::move(func));
+
+  auto f = first->then([minitial, sfunc](Try<ItT>& head) mutable {
+    return (*sfunc)(std::move(*minitial),
+                head.template get<IsTry::value, Arg&&>());
+  });
+
+  for (++first; first != last; ++first) {
+    f = collectAll(f, *first).then([sfunc](std::tuple<Try<T>, Try<ItT>>& t) {
+      return (*sfunc)(std::move(std::get<0>(t).value()),
+                  // Either return a ItT&& or a Try<ItT>&& depending
+                  // on the type of the argument of func.
+                  std::get<1>(t).template get<IsTry::value, Arg&&>());
+    });
+  }
+
+  return f;
+}
+
+// window (collection)
+
+template <class Collection, class F, class ItT, class Result>
+std::vector<Future<Result>>
+window(Collection input, F func, size_t n) {
+  struct WindowContext {
+    WindowContext(Collection&& i, F&& fn)
+        : input_(std::move(i)), promises_(input_.size()),
+          func_(std::move(fn))
+      {}
+    std::atomic<size_t> i_ {0};
+    Collection input_;
+    std::vector<Promise<Result>> promises_;
+    F func_;
+
+    static inline void spawn(const std::shared_ptr<WindowContext>& ctx) {
+      size_t i = ctx->i_++;
+      if (i < ctx->input_.size()) {
+        // Using setCallback_ directly since we don't need the Future
+        ctx->func_(std::move(ctx->input_[i])).setCallback_(
+          // ctx is captured by value
+          [ctx, i](Try<Result>&& t) {
+            ctx->promises_[i].setTry(std::move(t));
+            // Chain another future onto this one
+            spawn(std::move(ctx));
+          });
+      }
+    }
+  };
+
+  auto max = std::min(n, input.size());
+
+  auto ctx = std::make_shared<WindowContext>(
+    std::move(input), std::move(func));
+
+  for (size_t i = 0; i < max; ++i) {
+    // Start the first n Futures
+    WindowContext::spawn(ctx);
+  }
+
+  std::vector<Future<Result>> futures;
+  futures.reserve(ctx->promises_.size());
+  for (auto& promise : ctx->promises_) {
+    futures.emplace_back(promise.getFuture());
+  }
+
+  return futures;
+}
+
+// reduce
+
+template <class T>
+template <class I, class F>
+Future<I> Future<T>::reduce(I&& initial, F&& func) {
+  folly::MoveWrapper<I> minitial(std::move(initial));
+  folly::MoveWrapper<F> mfunc(std::move(func));
+  return then([minitial, mfunc](T& vals) mutable {
+    auto ret = std::move(*minitial);
+    for (auto& val : vals) {
+      ret = (*mfunc)(std::move(ret), std::move(val));
+    }
+    return ret;
+  });
+}
+
+// unorderedReduce (iterator)
+
+template <class It, class T, class F, class ItT, class Arg>
+Future<T> unorderedReduce(It first, It last, T initial, F func) {
+  if (first == last) {
+    return makeFuture(std::move(initial));
+  }
+
+  typedef isTry<Arg> IsTry;
+
+  struct UnorderedReduceContext {
+    UnorderedReduceContext(T&& memo, F&& fn, size_t n)
+        : lock_(), memo_(makeFuture<T>(std::move(memo))),
+          func_(std::move(fn)), numThens_(0), numFutures_(n), promise_()
+      {};
+    folly::MicroSpinLock lock_; // protects memo_ and numThens_
+    Future<T> memo_;
+    F func_;
+    size_t numThens_; // how many Futures completed and called .then()
+    size_t numFutures_; // how many Futures in total
+    Promise<T> promise_;
+  };
+
+  auto ctx = std::make_shared<UnorderedReduceContext>(
+    std::move(initial), std::move(func), std::distance(first, last));
+
+  mapSetCallback<ItT>(first, last, [ctx](size_t i, Try<ItT>&& t) {
+    folly::MoveWrapper<Try<ItT>> mt(std::move(t));
+    // Futures can be completed in any order, simultaneously.
+    // To make this non-blocking, we create a new Future chain in
+    // the order of completion to reduce the values.
+    // The spinlock just protects chaining a new Future, not actually
+    // executing the reduce, which should be really fast.
+    folly::MSLGuard lock(ctx->lock_);
+    ctx->memo_ = ctx->memo_.then([ctx, mt](T&& v) mutable {
+      // Either return a ItT&& or a Try<ItT>&& depending
+      // on the type of the argument of func.
+      return ctx->func_(std::move(v), mt->template get<IsTry::value, Arg&&>());
+    });
+    if (++ctx->numThens_ == ctx->numFutures_) {
+      // After reducing the value of the last Future, fulfill the Promise
+      ctx->memo_.setCallback_([ctx](Try<T>&& t2) {
+        ctx->promise_.setValue(std::move(t2));
+      });
+    }
+  });
+
+  return ctx->promise_.getFuture();
+}
+
+// within
+
+template <class T>
+Future<T> Future<T>::within(Duration dur, Timekeeper* tk) {
+  return within(dur, TimedOut(), tk);
+}
+
+template <class T>
+template <class E>
+Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
+
+  struct Context {
+    Context(E ex) : exception(std::move(ex)), promise() {}
+    E exception;
+    Future<Unit> thisFuture;
+    Promise<T> promise;
+    std::atomic<bool> token {false};
+  };
+
+  if (!tk) {
+    tk = folly::detail::getTimekeeperSingleton();
+  }
+
+  auto ctx = std::make_shared<Context>(std::move(e));
+
+  ctx->thisFuture = this->then([ctx](Try<T>&& t) mutable {
+    // TODO: "this" completed first, cancel "after"
+    if (ctx->token.exchange(true) == false) {
+      ctx->promise.setTry(std::move(t));
+    }
+  });
+
+  tk->after(dur).then([ctx](Try<Unit> const& t) mutable {
+    // "after" completed first, cancel "this"
+    ctx->thisFuture.raise(TimedOut());
+    if (ctx->token.exchange(true) == false) {
+      if (t.hasException()) {
+        ctx->promise.setException(std::move(t.exception()));
+      } else {
+        ctx->promise.setException(std::move(ctx->exception));
+      }
+    }
+  });
+
+  return ctx->promise.getFuture().via(getExecutor());
+}
+
+// delayed
+
+template <class T>
+Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
+  return collectAll(*this, futures::sleep(dur, tk))
+    .then([](std::tuple<Try<T>, Try<Unit>> tup) {
+      Try<T>& t = std::get<0>(tup);
+      return makeFuture<T>(std::move(t));
+    });
+}
+
+namespace detail {
+
+template <class T>
+void waitImpl(Future<T>& f) {
+  // short-circuit if there's nothing to do
+  if (f.isReady()) return;
+
+  folly::Baton<> baton;
+  f = f.then([&](Try<T> t) {
+    baton.post();
+    return makeFuture(std::move(t));
+  });
+  baton.wait();
+
+  // There's a race here between the return here and the actual finishing of
+  // the future. f is completed, but the setup may not have finished on done
+  // after the baton has posted.
+  while (!f.isReady()) {
+    std::this_thread::yield();
+  }
+}
+
+template <class T>
+void waitImpl(Future<T>& f, Duration dur) {
+  // short-circuit if there's nothing to do
+  if (f.isReady()) return;
+
+  auto baton = std::make_shared<folly::Baton<>>();
+  f = f.then([baton](Try<T> t) {
+    baton->post();
+    return makeFuture(std::move(t));
+  });
+
+  // Convert duration to a timepoint.
+  using clock_type = std::chrono::system_clock;
+  using duration_type = clock_type::duration;
+  auto tp = clock_type::now();
+  tp += dur;
+
+  // Let's preserve the invariant that if we did not timeout (timed_wait returns
+  // true), then the returned Future is complete when it is returned to the
+  // caller. We need to wait out the race for that Future to complete.
+  if (baton->timed_wait<clock_type, duration_type>(tp)) {
+    while (!f.isReady()) {
+      std::this_thread::yield();
+    }
+  }
+}
+
+template <class T>
+void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
+  while (!f.isReady()) {
+    e->drive();
+  }
+}
+
+} // detail
+
+template <class T>
+Future<T>& Future<T>::wait() & {
+  detail::waitImpl(*this);
+  return *this;
+}
+
+template <class T>
+Future<T>&& Future<T>::wait() && {
+  detail::waitImpl(*this);
+  return std::move(*this);
+}
+
+template <class T>
+Future<T>& Future<T>::wait(Duration dur) & {
+  detail::waitImpl(*this, dur);
+  return *this;
+}
+
+template <class T>
+Future<T>&& Future<T>::wait(Duration dur) && {
+  detail::waitImpl(*this, dur);
+  return std::move(*this);
+}
+
+template <class T>
+Future<T>& Future<T>::waitVia(DrivableExecutor* e) & {
+  detail::waitViaImpl(*this, e);
+  return *this;
+}
+
+template <class T>
+Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
+  detail::waitViaImpl(*this, e);
+  return std::move(*this);
+}
+
+template <class T>
+T Future<T>::get() {
+  return std::move(wait().value());
+}
+
+template <class T>
+T Future<T>::get(Duration dur) {
+  wait(dur);
+  if (isReady()) {
+    return std::move(value());
+  } else {
+    throw TimedOut();
+  }
+}
+
+template <class T>
+T Future<T>::getVia(DrivableExecutor* e) {
+  return std::move(waitVia(e).value());
+}
+
+namespace detail {
+  template <class T>
+  struct TryEquals {
+    static bool equals(const Try<T>& t1, const Try<T>& t2) {
+      return t1.value() == t2.value();
+    }
+  };
+}
+
+template <class T>
+Future<bool> Future<T>::willEqual(Future<T>& f) {
+  return collectAll(*this, f).then([](const std::tuple<Try<T>, Try<T>>& t) {
+    if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) {
+      return detail::TryEquals<T>::equals(std::get<0>(t), std::get<1>(t));
+    } else {
+      return false;
+      }
+  });
+}
+
+template <class T>
+template <class F>
+Future<T> Future<T>::filter(F predicate) {
+  auto p = folly::makeMoveWrapper(std::move(predicate));
+  return this->then([p](T val) {
+    T const& valConstRef = val;
+    if (!(*p)(valConstRef)) {
+      throw PredicateDoesNotObtain();
+    }
+    return val;
+  });
+}
+
+template <class T>
+template <class Callback>
+auto Future<T>::thenMulti(Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn))) {
+  // thenMulti with one callback is just a then
+  return then(std::forward<Callback>(fn));
+}
+
+template <class T>
+template <class Callback, class... Callbacks>
+auto Future<T>::thenMulti(Callback&& fn, Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...)) {
+  // thenMulti with two callbacks is just then(a).thenMulti(b, ...)
+  return then(std::forward<Callback>(fn)).
+         thenMulti(std::forward<Callbacks>(fns)...);
+}
+
+template <class T>
+template <class Callback, class... Callbacks, typename>
+auto Future<T>::thenMultiWithExecutor(Executor* x, Callback&& fn,
+                                      Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...)) {
+  // thenMultiExecutor with two callbacks is
+  // via(x).then(a).thenMulti(b, ...).via(oldX)
+  auto oldX = getExecutor();
+  setExecutor(x);
+  return then(std::forward<Callback>(fn)).
+         thenMulti(std::forward<Callbacks>(fns)...).via(oldX);
+}
+
+template <class T>
+template <class Callback>
+auto Future<T>::thenMultiWithExecutor(Executor* x, Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn))) {
+  // thenMulti with one callback is just a then with an executor
+  return then(x, std::forward<Callback>(fn));
+}
+
+template <class T>
+template <class Callback, class... Callbacks>
+auto Future<T>::thenMultiWithExecutor(Executor* x, const Cancellation &cx, Callback&& fn,
+                                      Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...)) {
+  // thenMultiExecutor with two callbacks is
+  // via(x).then(a).thenMulti(b, ...).via(oldX)
+  auto oldX = getExecutor();
+  setExecutor(x);
+  setCancellation(cx);
+  return then(std::forward<Callback>(fn)).
+         thenMulti(std::forward<Callbacks>(fns)...).via(oldX);
+}
+
+template <class T>
+template <class Callback>
+auto Future<T>::thenMultiWithExecutor(Executor* x, const Cancellation &cx, Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn))) {
+  // thenMulti with one callback is just a then with an executor
+  return then(x, cx, std::forward<Callback>(fn));
+}
+
+
+template <class T>
+template <class Callback, class... Callbacks, typename>
+auto Future<T>::thenMultiWithExecutor(ExecutorWithCancellation* x, Callback&& fn,
+                                      Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...)) {
+  // thenMultiExecutor with two callbacks is
+  // via(x).then(a).thenMulti(b, ...).via(oldX)
+  auto oldX = getExecutor();
+  setExecutor(x);
+  setCancellation(x->getCancellation());
+  return then(std::forward<Callback>(fn)).
+         thenMulti(std::forward<Callbacks>(fns)...).via(oldX);
+}
+
+template <class T>
+template <class Callback>
+auto Future<T>::thenMultiWithExecutor(ExecutorWithCancellation* x, Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn))) {
+  // thenMulti with one callback is just a then with an executor
+  return then(x, std::forward<Callback>(fn));
+}
+
+template <class F>
+inline Future<Unit> when(bool p, F thunk) {
+  return p ? thunk().unit() : makeFuture();
+}
+
+template <class P, class F>
+Future<Unit> whileDo(P predicate, F thunk) {
+  if (predicate()) {
+    return thunk().then([=] {
+      return whileDo(predicate, thunk);
+    });
+  }
+  return makeFuture();
+}
+
+template <class F>
+Future<Unit> times(const int n, F thunk) {
+  auto count = folly::makeMoveWrapper(
+    std::unique_ptr<std::atomic<int>>(new std::atomic<int>(0))
+  );
+  return folly::whileDo([=]() mutable {
+      return (*count)->fetch_add(1) < n;
+    }, thunk);
+}
+
+namespace futures {
+  template <class It, class F, class ItT, class Result>
+  std::vector<Future<Result>> map(It first, It last, F func) {
+    std::vector<Future<Result>> results;
+    for (auto it = first; it != last; it++) {
+      results.push_back(it->then(func));
+    }
+    return results;
+  }
+}
+
+namespace futures {
+
+namespace detail {
+
+struct retrying_policy_raw_tag {};
+struct retrying_policy_fut_tag {};
+
+template <class Policy>
+struct retrying_policy_traits {
+  using ew = exception_wrapper;
+  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_op_call, operator());
+  template <class Ret>
+  using has_op = typename std::integral_constant<bool,
+        has_op_call<Policy, Ret(size_t, const ew&)>::value ||
+        has_op_call<Policy, Ret(size_t, const ew&) const>::value>;
+  using is_raw = has_op<bool>;
+  using is_fut = has_op<Future<bool>>;
+  using tag = typename std::conditional<
+        is_raw::value, retrying_policy_raw_tag, typename std::conditional<
+        is_fut::value, retrying_policy_fut_tag, void>::type>::type;
+};
+
+template <class Policy, class FF>
+typename std::result_of<FF(size_t)>::type
+retrying(size_t k, Policy&& p, FF&& ff) {
+  using F = typename std::result_of<FF(size_t)>::type;
+  using T = typename F::value_type;
+  auto f = ff(k++);
+  auto pm = makeMoveWrapper(p);
+  auto ffm = makeMoveWrapper(ff);
+  return f.onError([=](exception_wrapper x) mutable {
+      auto q = (*pm)(k, x);
+      auto xm = makeMoveWrapper(std::move(x));
+      return q.then([=](bool r) mutable {
+          return r
+            ? retrying(k, pm.move(), ffm.move())
+            : makeFuture<T>(xm.move());
+      });
+  });
+}
+
+template <class Policy, class FF>
+typename std::result_of<FF(size_t)>::type
+retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) {
+  auto pm = makeMoveWrapper(std::move(p));
+  auto q = [=](size_t k, exception_wrapper x) {
+    return makeFuture<bool>((*pm)(k, x));
+  };
+  return retrying(0, std::move(q), std::forward<FF>(ff));
+}
+
+template <class Policy, class FF>
+typename std::result_of<FF(size_t)>::type
+retrying(Policy&& p, FF&& ff, retrying_policy_fut_tag) {
+  return retrying(0, std::forward<Policy>(p), std::forward<FF>(ff));
+}
+
+//  jittered exponential backoff, clamped to [backoff_min, backoff_max]
+template <class URNG>
+Duration retryingJitteredExponentialBackoffDur(
+    size_t n,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param,
+    URNG& rng) {
+  using d = Duration;
+  auto dist = std::normal_distribution<double>(0.0, jitter_param);
+  auto jitter = std::exp(dist(rng));
+  auto backoff = d(d::rep(jitter * backoff_min.count() * std::pow(2, n - 1)));
+  return std::max(backoff_min, std::min(backoff_max, backoff));
+}
+
+template <class Policy, class URNG>
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param,
+    URNG rng,
+    Policy&& p) {
+  auto pm = makeMoveWrapper(std::move(p));
+  auto rngp = std::make_shared<URNG>(std::move(rng));
+  return [=](size_t n, const exception_wrapper& ex) mutable {
+    if (n == max_tries) { return makeFuture(false); }
+    return (*pm)(n, ex).then([=](bool v) {
+        if (!v) { return makeFuture(false); }
+        auto backoff = detail::retryingJitteredExponentialBackoffDur(
+            n, backoff_min, backoff_max, jitter_param, *rngp);
+        return futures::sleep(backoff).then([] { return true; });
+    });
+  };
+}
+
+template <class Policy, class URNG>
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param,
+    URNG rng,
+    Policy&& p,
+    retrying_policy_raw_tag) {
+  auto pm = makeMoveWrapper(std::move(p));
+  auto q = [=](size_t n, const exception_wrapper& e) {
+    return makeFuture((*pm)(n, e));
+  };
+  return retryingPolicyCappedJitteredExponentialBackoff(
+      max_tries,
+      backoff_min,
+      backoff_max,
+      jitter_param,
+      std::move(rng),
+      std::move(q));
+}
+
+template <class Policy, class URNG>
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param,
+    URNG rng,
+    Policy&& p,
+    retrying_policy_fut_tag) {
+  return retryingPolicyCappedJitteredExponentialBackoff(
+      max_tries,
+      backoff_min,
+      backoff_max,
+      jitter_param,
+      std::move(rng),
+      std::move(p));
+}
+
+}
+
+template <class Policy, class FF>
+typename std::result_of<FF(size_t)>::type
+retrying(Policy&& p, FF&& ff) {
+  using tag = typename detail::retrying_policy_traits<Policy>::tag;
+  return detail::retrying(std::forward<Policy>(p), std::forward<FF>(ff), tag());
+}
+
+inline
+std::function<bool(size_t, const exception_wrapper&)>
+retryingPolicyBasic(
+    size_t max_tries) {
+  return [=](size_t n, const exception_wrapper&) { return n < max_tries; };
+}
+
+template <class Policy, class URNG>
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param,
+    URNG rng,
+    Policy&& p) {
+  using tag = typename detail::retrying_policy_traits<Policy>::tag;
+  return detail::retryingPolicyCappedJitteredExponentialBackoff(
+      max_tries,
+      backoff_min,
+      backoff_max,
+      jitter_param,
+      std::move(rng),
+      std::move(p),
+      tag());
+}
+
+inline
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param) {
+  auto p = [](size_t, const exception_wrapper&) { return true; };
+  return retryingPolicyCappedJitteredExponentialBackoff(
+      max_tries,
+      backoff_min,
+      backoff_max,
+      jitter_param,
+      ThreadLocalPRNG(),
+      std::move(p));
+}
+
+}
+
+// Instantiate the most common Future types to save compile time
+extern template class Future<Unit>;
+extern template class Future<bool>;
+extern template class Future<int>;
+extern template class Future<int64_t>;
+extern template class Future<std::string>;
+extern template class Future<double>;
+
+} // namespace folly
diff --git a/faux-folly/folly/futures/Future-pre.h b/faux-folly/folly/futures/Future-pre.h
new file mode 100644
index 0000000..f1a07d5
--- /dev/null
+++ b/faux-folly/folly/futures/Future-pre.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015 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
+
+// included by Future.h, do not include directly.
+
+namespace folly {
+
+template <class> class Promise;
+
+template <typename T>
+struct isFuture : std::false_type {
+  using Inner = typename Unit::Lift<T>::type;
+};
+
+template <typename T>
+struct isFuture<Future<T>> : std::true_type {
+  typedef T Inner;
+};
+
+template <typename T>
+struct isTry : std::false_type {};
+
+template <typename T>
+struct isTry<Try<T>> : std::true_type {};
+
+namespace detail {
+
+template <class> class Core;
+template <class...> struct CollectAllVariadicContext;
+template <class...> struct CollectVariadicContext;
+template <class> struct CollectContext;
+
+template<typename F, typename... Args>
+using resultOf = decltype(std::declval<F>()(std::declval<Args>()...));
+
+template <typename...>
+struct ArgType;
+
+template <typename Arg, typename... Args>
+struct ArgType<Arg, Args...> {
+  typedef Arg FirstArg;
+};
+
+template <>
+struct ArgType<> {
+  typedef void FirstArg;
+};
+
+template <bool isTry, typename F, typename... Args>
+struct argResult {
+  using Result = resultOf<F, Args...>;
+};
+
+template<typename F, typename... Args>
+struct callableWith {
+    template<typename T,
+             typename = detail::resultOf<T, Args...>>
+    static constexpr std::true_type
+    check(std::nullptr_t) { return std::true_type{}; };
+
+    template<typename>
+    static constexpr std::false_type
+    check(...) { return std::false_type{}; };
+
+    typedef decltype(check<F>(nullptr)) type;
+    static constexpr bool value = type::value;
+};
+
+template<typename T, typename F>
+struct callableResult {
+  typedef typename std::conditional<
+    callableWith<F>::value,
+    detail::argResult<false, F>,
+    typename std::conditional<
+      callableWith<F, T&&>::value,
+      detail::argResult<false, F, T&&>,
+      typename std::conditional<
+        callableWith<F, T&>::value,
+        detail::argResult<false, F, T&>,
+        typename std::conditional<
+          callableWith<F, Try<T>&&>::value,
+          detail::argResult<true, F, Try<T>&&>,
+          detail::argResult<true, F, Try<T>&>>::type>::type>::type>::type Arg;
+  typedef isFuture<typename Arg::Result> ReturnsFuture;
+  typedef Future<typename ReturnsFuture::Inner> Return;
+};
+
+template <typename L>
+struct Extract : Extract<decltype(&L::operator())> { };
+
+template <typename Class, typename R, typename... Args>
+struct Extract<R(Class::*)(Args...) const> {
+  typedef isFuture<R> ReturnsFuture;
+  typedef Future<typename ReturnsFuture::Inner> Return;
+  typedef typename ReturnsFuture::Inner RawReturn;
+  typedef typename ArgType<Args...>::FirstArg FirstArg;
+};
+
+template <typename Class, typename R, typename... Args>
+struct Extract<R(Class::*)(Args...)> {
+  typedef isFuture<R> ReturnsFuture;
+  typedef Future<typename ReturnsFuture::Inner> Return;
+  typedef typename ReturnsFuture::Inner RawReturn;
+  typedef typename ArgType<Args...>::FirstArg FirstArg;
+};
+
+} // detail
+
+
+class Timekeeper;
+
+} // namespace
diff --git a/faux-folly/folly/futures/Future.cpp b/faux-folly/folly/futures/Future.cpp
new file mode 100644
index 0000000..501322a
--- /dev/null
+++ b/faux-folly/folly/futures/Future.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 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/futures/Future.h>
+#include <folly/futures/detail/ThreadWheelTimekeeper.h>
+#include <folly/Likely.h>
+
+namespace folly {
+
+// Instantiate the most common Future types to save compile time
+template class Future<Unit>;
+template class Future<bool>;
+template class Future<int>;
+template class Future<int64_t>;
+template class Future<std::string>;
+template class Future<double>;
+
+}
+
+namespace folly { namespace futures {
+
+Future<Unit> sleep(Duration dur, Timekeeper* tk) {
+  if (LIKELY(!tk)) {
+    tk = folly::detail::getTimekeeperSingleton();
+  }
+  return tk->after(dur);
+}
+
+}}
diff --git a/faux-folly/folly/futures/Future.h b/faux-folly/folly/futures/Future.h
new file mode 100644
index 0000000..5575d4a
--- /dev/null
+++ b/faux-folly/folly/futures/Future.h
@@ -0,0 +1,581 @@
+/*
+ * Copyright 2015 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 <memory>
+#include <type_traits>
+#include <vector>
+
+#include <folly/Cancellation.h>
+#include <folly/Optional.h>
+#include <folly/Portability.h>
+#include <folly/MoveWrapper.h>
+#include <folly/futures/DrivableExecutor.h>
+#include <folly/futures/Promise.h>
+#include <folly/futures/Try.h>
+#include <folly/futures/FutureException.h>
+#include <folly/futures/detail/Types.h>
+
+// boring predeclarations and details
+#include <folly/futures/Future-pre.h>
+
+// not-boring helpers, e.g. all in folly::futures, makeFuture variants, etc.
+// Needs to be included after Future-pre.h and before Future-inl.h
+#include <folly/futures/helpers.h>
+
+namespace folly {
+
+class Cancellation;
+class ExecutorWithCancellation;
+
+template<class T>
+struct has_cancellation {
+    protected:
+        static constexpr std::false_type detect(...);
+        template <class U> static decltype(std::declval<U>().getCancellation()) detect(const U&);
+    public:
+        static constexpr bool value = std::is_same<Cancellation&, decltype(detect(std::declval<T>()))>::value;
+};
+
+template <class T>
+class Future {
+ public:
+  typedef T value_type;
+
+  // not copyable
+  Future(Future const&) = delete;
+  Future& operator=(Future const&) = delete;
+
+  // movable
+  Future(Future&&) noexcept;
+  Future& operator=(Future&&) noexcept;
+
+  /// Construct a Future from a value (perfect forwarding)
+  template <class T2 = T, typename =
+            typename std::enable_if<
+              !isFuture<typename std::decay<T2>::type>::value>::type>
+  /* implicit */ Future(T2&& val);
+
+  template <class T2 = T, typename =
+            typename std::enable_if<
+              std::is_same<Unit, T2>::value>::type>
+  Future();
+
+  ~Future();
+
+  /** Return the reference to result. Should not be called if !isReady().
+    Will rethrow the exception if an exception has been
+    captured.
+    */
+  typename std::add_lvalue_reference<T>::type
+  value();
+  typename std::add_lvalue_reference<const T>::type
+  value() const;
+
+  /// Returns an inactive Future which will call back on the other side of
+  /// executor (when it is activated).
+  ///
+  /// NB remember that Futures activate when they destruct. This is good,
+  /// it means that this will work:
+  ///
+  ///   f.via(e).then(a).then(b);
+  ///
+  /// a and b will execute in the same context (the far side of e), because
+  /// the Future (temporary variable) created by via(e) does not call back
+  /// until it destructs, which is after then(a) and then(b) have been wired
+  /// up.
+  ///
+  /// But this is still racy:
+  ///
+  ///   f = f.via(e).then(a);
+  ///   f.then(b);
+  // The ref-qualifier allows for `this` to be moved out so we
+  // don't get access-after-free situations in chaining.
+  // https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/
+  inline Future<T> via(
+      Executor* executor,
+      int8_t priority = Executor::MID_PRI) &&;
+
+  /// This variant creates a new future, where the ref-qualifier && version
+  /// moves `this` out. This one is less efficient but avoids confusing users
+  /// when "return f.via(x);" fails.
+  inline Future<T> via(
+      Executor* executor,
+      int8_t priority = Executor::MID_PRI) &;
+
+  inline Future<T> via(
+      Executor* executor,
+      const Cancellation& cx,
+      int8_t priority = Executor::MID_PRI) &&;
+
+  inline Future<T> via(
+      Executor* executor,
+      const Cancellation& cx,
+      int8_t priority = Executor::MID_PRI) &;
+
+  inline Future<T> via(
+      ExecutorWithCancellation* executor,
+      int8_t priority = Executor::MID_PRI) &&;
+
+  inline Future<T> via(
+      ExecutorWithCancellation* executor,
+      int8_t priority = Executor::MID_PRI) &;
+
+  /** True when the result (or exception) is ready. */
+  bool isReady() const;
+
+  /// sugar for getTry().hasValue()
+  bool hasValue();
+
+  /// sugar for getTry().hasException()
+  bool hasException();
+
+  /** A reference to the Try of the value */
+  Try<T>& getTry();
+
+  /// If the promise has been fulfilled, return an Optional with the Try<T>.
+  /// Otherwise return an empty Optional.
+  /// Note that this moves the Try<T> out.
+  Optional<Try<T>> poll();
+
+  /// Block until the future is fulfilled. Returns the value (moved out), or
+  /// throws the exception. The future must not already have a callback.
+  T get();
+
+  /// Block until the future is fulfilled, or until timed out. Returns the
+  /// value (moved out), or throws the exception (which might be a TimedOut
+  /// exception).
+  T get(Duration dur);
+
+  /// Call e->drive() repeatedly until the future is fulfilled. Examples
+  /// of DrivableExecutor include EventBase and ManualExecutor. Returns the
+  /// value (moved out), or throws the exception.
+  T getVia(DrivableExecutor* e);
+
+  /// Unwraps the case of a Future<Future<T>> instance, and returns a simple
+  /// Future<T> instance.
+  template <class F = T>
+  typename std::enable_if<isFuture<F>::value,
+                          Future<typename isFuture<T>::Inner>>::type
+  unwrap();
+
+  /** When this Future has completed, execute func which is a function that
+    takes one of:
+      (const) Try<T>&&
+      (const) Try<T>&
+      (const) Try<T>
+      (const) T&&
+      (const) T&
+      (const) T
+      (void)
+
+    Func shall return either another Future or a value.
+
+    A Future for the return type of func is returned.
+
+    Future<string> f2 = f1.then([](Try<T>&&) { return string("foo"); });
+
+    The Future given to the functor is ready, and the functor may call
+    value(), which may rethrow if this has captured an exception. If func
+    throws, the exception will be captured in the Future that is returned.
+    */
+  template <typename F, typename R = detail::callableResult<T, F>>
+  typename R::Return then(F func) {
+    typedef typename R::Arg Arguments;
+    return thenImplementation<F, R>(std::move(func), Arguments());
+  }
+
+  /// Variant where func is an member function
+  ///
+  ///   struct Worker { R doWork(Try<T>); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(&Worker::doWork, w);
+  ///
+  /// This is just sugar for
+  ///
+  ///   f1.then(std::bind(&Worker::doWork, w));
+  template <typename R, typename Caller, typename... Args>
+  Future<typename isFuture<R>::Inner>
+  then(R(Caller::*func)(Args...), Caller *instance);
+
+  /// Execute the callback via the given Executor. The executor doesn't stick.
+  ///
+  /// Contrast
+  ///
+  ///   f.via(x).then(b).then(c)
+  ///
+  /// with
+  ///
+  ///   f.then(x, b).then(c)
+  ///
+  /// In the former both b and c execute via x. In the latter, only b executes
+  /// via x, and c executes via the same executor (if any) that f had.
+  template <class Executor, class Arg, class... Args>
+  auto then(Executor* x, Arg&& arg, Args&&... args)
+    -> typename std::enable_if<!has_cancellation<Executor>::value,
+                                decltype(this->then(std::forward<Arg>(arg),
+                                                    std::forward<Args>(args)...))>::type;
+
+  template <class Executor, class Arg, class... Args>
+  auto then(Executor* x, Arg&& arg, Args&&... args)
+    -> typename std::enable_if<has_cancellation<Executor>::value,
+                                decltype(this->then(std::forward<Arg>(arg),
+                                                    std::forward<Args>(args)...))>::type;
+  /// Execute the callback via the given Executor with Cancellation. The executor doesn't stick.
+  ///
+  /// Contrast
+  ///
+  ///   f.via(x, cx).then(b)
+  ///
+  /// with
+  ///
+  ///   f.then(x, cx, b)
+  ///
+  /// In the former both b and c execute via x. In the latter, only b executes
+  /// via x, and c executes via the same executor (if any) that f had.
+  template <class Executor, class Arg, class... Args>
+  auto then(Executor* x, const Cancellation &cx, Arg&& arg, Args&&... args)
+    -> decltype(this->then(std::forward<Arg>(arg),
+                           std::forward<Args>(args)...));
+
+  /// Convenience method for ignoring the value and creating a Future<Unit>.
+  /// Exceptions still propagate.
+  Future<Unit> then();
+
+  /// Set an error callback for this Future. The callback should take a single
+  /// argument of the type that you want to catch, and should return a value of
+  /// the same type as this Future, or a Future of that type (see overload
+  /// below). For instance,
+  ///
+  /// makeFuture()
+  ///   .then([] {
+  ///     throw std::runtime_error("oh no!");
+  ///     return 42;
+  ///   })
+  ///   .onError([] (std::runtime_error& e) {
+  ///     LOG(INFO) << "std::runtime_error: " << e.what();
+  ///     return -1; // or makeFuture<int>(-1)
+  ///   });
+  template <class F>
+  typename std::enable_if<
+    !detail::callableWith<F, exception_wrapper>::value &&
+    !detail::Extract<F>::ReturnsFuture::value,
+    Future<T>>::type
+  onError(F&& func);
+
+  /// Overload of onError where the error callback returns a Future<T>
+  template <class F>
+  typename std::enable_if<
+    !detail::callableWith<F, exception_wrapper>::value &&
+    detail::Extract<F>::ReturnsFuture::value,
+    Future<T>>::type
+  onError(F&& func);
+
+  /// Overload of onError that takes exception_wrapper and returns Future<T>
+  template <class F>
+  typename std::enable_if<
+    detail::callableWith<F, exception_wrapper>::value &&
+    detail::Extract<F>::ReturnsFuture::value,
+    Future<T>>::type
+  onError(F&& func);
+
+  /// Overload of onError that takes exception_wrapper and returns T
+  template <class F>
+  typename std::enable_if<
+    detail::callableWith<F, exception_wrapper>::value &&
+    !detail::Extract<F>::ReturnsFuture::value,
+    Future<T>>::type
+  onError(F&& func);
+
+  /// Convenience function to allow exceptions to throw in Executor
+  void onErrorThrowIn(Executor *x);
+
+  /// func is like std::function<void()> and is executed unconditionally, and
+  /// the value/exception is passed through to the resulting Future.
+  /// func shouldn't throw, but if it does it will be captured and propagated,
+  /// and discard any value/exception that this Future has obtained.
+  template <class F>
+  Future<T> ensure(F func);
+
+  /// Like onError, but for timeouts. example:
+  ///
+  ///   Future<int> f = makeFuture<int>(42)
+  ///     .delayed(long_time)
+  ///     .onTimeout(short_time,
+  ///       []() -> int{ return -1; });
+  ///
+  /// or perhaps
+  ///
+  ///   Future<int> f = makeFuture<int>(42)
+  ///     .delayed(long_time)
+  ///     .onTimeout(short_time,
+  ///       []() { return makeFuture<int>(some_exception); });
+  template <class F>
+  Future<T> onTimeout(Duration, F&& func, Timekeeper* = nullptr);
+
+  /// This is not the method you're looking for.
+  ///
+  /// This needs to be public because it's used by make* and when*, and it's
+  /// not worth listing all those and their fancy template signatures as
+  /// friends. But it's not for public consumption.
+  template <class F>
+  void setCallback_(F&& func);
+
+  /// A Future's callback is executed when all three of these conditions have
+  /// become true: it has a value (set by the Promise), it has a callback (set
+  /// by then), and it is active (active by default).
+  ///
+  /// Inactive Futures will activate upon destruction.
+  FOLLY_DEPRECATED("do not use") Future<T>& activate() & {
+    core_->activate();
+    return *this;
+  }
+  FOLLY_DEPRECATED("do not use") Future<T>& deactivate() & {
+    core_->deactivate();
+    return *this;
+  }
+  FOLLY_DEPRECATED("do not use") Future<T> activate() && {
+    core_->activate();
+    return std::move(*this);
+  }
+  FOLLY_DEPRECATED("do not use") Future<T> deactivate() && {
+    core_->deactivate();
+    return std::move(*this);
+  }
+
+  bool isActive() {
+    return core_->isActive();
+  }
+
+  template <class E>
+  void raise(E&& exception) {
+    raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
+        std::move(exception)));
+  }
+
+  /// Raise an interrupt. If the promise holder has an interrupt
+  /// handler it will be called and potentially stop asynchronous work from
+  /// being done. This is advisory only - a promise holder may not set an
+  /// interrupt handler, or may do anything including ignore. But, if you know
+  /// your future supports this the most likely result is stopping or
+  /// preventing the asynchronous operation (if in time), and the promise
+  /// holder setting an exception on the future. (That may happen
+  /// asynchronously, of course.)
+  void raise(exception_wrapper interrupt);
+
+  void cancel() {
+    raise(FutureCancellation());
+  }
+
+  /// Throw TimedOut if this Future does not complete within the given
+  /// duration from now. The optional Timeekeeper is as with futures::sleep().
+  Future<T> within(Duration, Timekeeper* = nullptr);
+
+  /// Throw the given exception if this Future does not complete within the
+  /// given duration from now. The optional Timeekeeper is as with
+  /// futures::sleep().
+  template <class E>
+  Future<T> within(Duration, E exception, Timekeeper* = nullptr);
+
+  /// Delay the completion of this Future for at least this duration from
+  /// now. The optional Timekeeper is as with futures::sleep().
+  Future<T> delayed(Duration, Timekeeper* = nullptr);
+
+  /// Block until this Future is complete. Returns a reference to this Future.
+  Future<T>& wait() &;
+
+  /// Overload of wait() for rvalue Futures
+  Future<T>&& wait() &&;
+
+  /// Block until this Future is complete or until the given Duration passes.
+  /// Returns a reference to this Future
+  Future<T>& wait(Duration) &;
+
+  /// Overload of wait(Duration) for rvalue Futures
+  Future<T>&& wait(Duration) &&;
+
+  /// Call e->drive() repeatedly until the future is fulfilled. Examples
+  /// of DrivableExecutor include EventBase and ManualExecutor. Returns a
+  /// reference to this Future so that you can chain calls if desired.
+  /// value (moved out), or throws the exception.
+  Future<T>& waitVia(DrivableExecutor* e) &;
+
+  /// Overload of waitVia() for rvalue Futures
+  Future<T>&& waitVia(DrivableExecutor* e) &&;
+
+  /// If the value in this Future is equal to the given Future, when they have
+  /// both completed, the value of the resulting Future<bool> will be true. It
+  /// will be false otherwise (including when one or both Futures have an
+  /// exception)
+  Future<bool> willEqual(Future<T>&);
+
+  /// predicate behaves like std::function<bool(T const&)>
+  /// If the predicate does not obtain with the value, the result
+  /// is a folly::PredicateDoesNotObtain exception
+  template <class F>
+  Future<T> filter(F predicate);
+
+  /// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example
+  /// the result of collect or collectAll
+  template <class I, class F>
+  Future<I> reduce(I&& initial, F&& func);
+
+  /// Create a Future chain from a sequence of callbacks. i.e.
+  ///
+  ///   f.then(a).then(b).then(c)
+  ///
+  /// where f is a Future<A> and the result of the chain is a Future<D>
+  /// becomes
+  ///
+  ///   f.thenMulti(a, b, c);
+  template <class Callback, class... Callbacks>
+  auto thenMulti(Callback&& fn, Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...));
+
+  // Nothing to see here, just thenMulti's base case
+  template <class Callback>
+  auto thenMulti(Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn)));
+
+  /// Create a Future chain from a sequence of callbacks. i.e.
+  ///
+  ///   f.via(executor).then(a).then(b).then(c).via(oldExecutor)
+  ///
+  /// where f is a Future<A> and the result of the chain is a Future<D>
+  /// becomes
+  ///
+  ///   f.thenMultiWithExecutor(executor, a, b, c);
+  template <class Callback, class... Callbacks,
+            typename = typename std::enable_if<!std::is_base_of<Cancellation,
+                                               typename std::remove_reference<Callback>::type>::value >::type>
+  auto thenMultiWithExecutor(Executor* x, Callback&& fn, Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...));
+
+  // Nothing to see here, just thenMultiWithExecutor's base case
+  template <class Callback>
+  auto thenMultiWithExecutor(Executor* x, Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn)));
+
+  /// Create a Future chain from a sequence of callbacks with Cancellation
+  ///
+  ///   f.via(executor, cx).then(a).then(b).then(c).via(oldExecutor)
+  ///
+  /// where f is a Future<A> and the result of the chain is a Future<D>
+  /// becomes
+  ///
+  ///   f.thenMultiWithExecutor(executor, cx, a, b, c);
+  template <class Callback, class... Callbacks>
+  auto thenMultiWithExecutor(Executor* x, const Cancellation &cx, Callback&& fn, Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...));
+  // Nothing to see here, just thenMultiWithExecutor's base case
+  template <class Callback>
+  auto thenMultiWithExecutor(Executor* x, const Cancellation &cx, Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn)));
+
+  // ExecutorWithCancellation
+  template <class Callback, class... Callbacks,
+            typename = typename std::enable_if<!std::is_base_of<Cancellation,
+                                               typename std::remove_reference<Callback>::type>::value >::type>
+  auto thenMultiWithExecutor(ExecutorWithCancellation* x, Callback&& fn, Callbacks&&... fns)
+    -> decltype(this->then(std::forward<Callback>(fn)).
+                      thenMulti(std::forward<Callbacks>(fns)...));
+
+  // Nothing to see here, just thenMultiWithExecutor's base case
+  template <class Callback>
+  auto thenMultiWithExecutor(ExecutorWithCancellation* x, Callback&& fn)
+    -> decltype(this->then(std::forward<Callback>(fn)));
+
+  /// Discard a result, but propagate an exception.
+  Future<Unit> unit() {
+    return then([]{ return Unit{}; });
+  }
+
+ protected:
+  typedef detail::Core<T>* corePtr;
+
+  // shared core state object
+  corePtr core_;
+
+  explicit
+  Future(corePtr obj) : core_(obj) {}
+
+  void detach();
+
+  void throwIfInvalid() const;
+
+  friend class Promise<T>;
+  template <class> friend class Future;
+
+  template <class T2>
+  friend Future<T2> makeFuture(Try<T2>&&);
+
+  /// Repeat the given future (i.e., the computation it contains)
+  /// n times.
+  ///
+  /// thunk behaves like std::function<Future<T2>(void)>
+  template <class F>
+  friend Future<Unit> times(const int n, F thunk);
+
+  /// Carry out the computation contained in the given future if
+  /// the predicate holds.
+  ///
+  /// thunk behaves like std::function<Future<T2>(void)>
+  template <class F>
+  friend Future<Unit> when(bool p, F thunk);
+
+  /// Carry out the computation contained in the given future if
+  /// while the predicate continues to hold.
+  ///
+  /// thunk behaves like std::function<Future<T2>(void)>
+  ///
+  /// predicate behaves like std::function<bool(void)>
+  template <class P, class F>
+  friend Future<Unit> whileDo(P predicate, F thunk);
+
+  // Variant: returns a value
+  // e.g. f.then([](Try<T> t){ return t.value(); });
+  template <typename F, typename R, bool isTry, typename... Args>
+  typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
+  thenImplementation(F func, detail::argResult<isTry, F, Args...>);
+
+  // Variant: returns a Future
+  // e.g. f.then([](Try<T> t){ return makeFuture<T>(t); });
+  template <typename F, typename R, bool isTry, typename... Args>
+  typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
+  thenImplementation(F func, detail::argResult<isTry, F, Args...>);
+
+  Executor* getExecutor() { return core_->getExecutor(); }
+  void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
+    core_->setExecutor(x, priority);
+  }
+
+  void setCancellation(Cancellation cx) {
+    core_->setCancellation(std::move(cx));
+  }
+
+};
+
+} // folly
+
+#include <folly/futures/Future-inl.h>
diff --git a/faux-folly/folly/futures/FutureException.h b/faux-folly/folly/futures/FutureException.h
new file mode 100644
index 0000000..1e004dc
--- /dev/null
+++ b/faux-folly/folly/futures/FutureException.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 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 <exception>
+#include <string>
+
+namespace folly {
+
+class FutureException : public std::exception {
+
+public:
+
+  explicit FutureException(std::string message_arg)
+    : message(message_arg) {}
+
+  ~FutureException() throw(){}
+
+  virtual const char *what() const throw() {
+    return message.c_str();
+  }
+
+  bool operator==(const FutureException &other) const{
+    return other.message == this->message;
+  }
+
+  bool operator!=(const FutureException &other) const{
+    return !(*this == other);
+  }
+
+  protected:
+    std::string message;
+};
+
+class BrokenPromise : public FutureException {
+  public:
+    explicit BrokenPromise() :
+      FutureException("Broken promise") { }
+};
+
+class NoState : public FutureException {
+  public:
+    explicit NoState() : FutureException("No state") { }
+};
+
+class PromiseAlreadySatisfied : public FutureException {
+  public:
+    explicit PromiseAlreadySatisfied() :
+      FutureException("Promise already satisfied") { }
+};
+
+class FutureNotReady : public FutureException {
+  public:
+    explicit FutureNotReady() :
+      FutureException("Future not ready") { }
+};
+
+class FutureAlreadyRetrieved : public FutureException {
+  public:
+    explicit FutureAlreadyRetrieved () :
+      FutureException("Future already retrieved") { }
+};
+
+class UsingUninitializedTry : public FutureException {
+  public:
+    explicit UsingUninitializedTry() :
+      FutureException("Using unitialized try") { }
+};
+
+class FutureCancellation : public FutureException {
+ public:
+  FutureCancellation() : FutureException("Future was cancelled") {}
+};
+
+class TimedOut : public FutureException {
+ public:
+  TimedOut() : FutureException("Timed out") {}
+};
+
+class PredicateDoesNotObtain : public FutureException {
+ public:
+  PredicateDoesNotObtain() : FutureException("Predicate does not obtain") {}
+};
+
+}
diff --git a/faux-folly/folly/futures/InlineExecutor.cpp b/faux-folly/folly/futures/InlineExecutor.cpp
new file mode 100644
index 0000000..75271f9
--- /dev/null
+++ b/faux-folly/folly/futures/InlineExecutor.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2015 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.
+ */
diff --git a/faux-folly/folly/futures/InlineExecutor.h b/faux-folly/folly/futures/InlineExecutor.h
new file mode 100644
index 0000000..bcb393c
--- /dev/null
+++ b/faux-folly/folly/futures/InlineExecutor.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 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/Executor.h>
+
+namespace folly {
+
+  /// When work is "queued", execute it immediately inline.
+  /// Usually when you think you want this, you actually want a
+  /// QueuedImmediateExecutor.
+  class InlineExecutor : public Executor {
+   public:
+    void add(Func f) override {
+      f();
+    }
+  };
+
+}
diff --git a/faux-folly/folly/futures/ManualExecutor.cpp b/faux-folly/folly/futures/ManualExecutor.cpp
new file mode 100644
index 0000000..9a0d1e7
--- /dev/null
+++ b/faux-folly/folly/futures/ManualExecutor.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 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/futures/ManualExecutor.h>
+
+#include <string.h>
+#include <string>
+#include <tuple>
+
+#include <stdexcept>
+
+namespace folly {
+
+void ManualExecutor::add(Func callback) {
+  std::lock_guard<std::mutex> lock(lock_);
+  funcs_.push(std::move(callback));
+  sem_.post();
+}
+
+size_t ManualExecutor::run() {
+  size_t count;
+  size_t n;
+  Func func;
+
+  {
+    std::lock_guard<std::mutex> lock(lock_);
+
+    while (!scheduledFuncs_.empty()) {
+      auto& sf = scheduledFuncs_.top();
+      if (sf.time > now_)
+        break;
+      funcs_.push(sf.func);
+      scheduledFuncs_.pop();
+    }
+
+    n = funcs_.size();
+  }
+
+  for (count = 0; count < n; count++) {
+    {
+      std::lock_guard<std::mutex> lock(lock_);
+      if (funcs_.empty()) {
+        break;
+      }
+
+      // Balance the semaphore so it doesn't grow without bound
+      // if nobody is calling wait().
+      // This may fail (with EAGAIN), that's fine.
+      sem_.tryWait();
+
+      func = std::move(funcs_.front());
+      funcs_.pop();
+    }
+    func();
+  }
+
+  return count;
+}
+
+void ManualExecutor::wait() {
+  while (true) {
+    {
+      std::lock_guard<std::mutex> lock(lock_);
+      if (!funcs_.empty())
+        break;
+    }
+
+    sem_.wait();
+  }
+}
+
+void ManualExecutor::advanceTo(TimePoint const& t) {
+  if (t > now_) {
+    now_ = t;
+  }
+  run();
+}
+
+} // folly
diff --git a/faux-folly/folly/futures/ManualExecutor.h b/faux-folly/folly/futures/ManualExecutor.h
new file mode 100644
index 0000000..afdf059
--- /dev/null
+++ b/faux-folly/folly/futures/ManualExecutor.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2015 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/LifoSem.h>
+#include <folly/futures/DrivableExecutor.h>
+#include <folly/futures/ScheduledExecutor.h>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <cstdio>
+
+namespace folly {
+  /// A ManualExecutor only does work when you turn the crank, by calling
+  /// run() or indirectly with makeProgress() or waitFor().
+  ///
+  /// The clock for a manual executor starts at 0 and advances only when you
+  /// ask it to. i.e. time is also under manual control.
+  ///
+  /// NB No attempt has been made to make anything other than add and schedule
+  /// threadsafe.
+  class ManualExecutor : public DrivableExecutor,
+                         public ScheduledExecutor {
+   public:
+    void add(Func) override;
+
+    /// Do work. Returns the number of functions that were executed (maybe 0).
+    /// Non-blocking, in the sense that we don't wait for work (we can't
+    /// control whether one of the functions blocks).
+    /// This is stable, it will not chase an ever-increasing tail of work.
+    /// This also means, there may be more work available to perform at the
+    /// moment that this returns.
+    size_t run();
+
+    /// Wait for work to do.
+    void wait();
+
+    /// Wait for work to do, and do it.
+    void makeProgress() {
+      wait();
+      run();
+    }
+
+    /// Implements DrivableExecutor
+    void drive() override {
+      makeProgress();
+    }
+
+    /// makeProgress until this Future is ready.
+    template <class F> void waitFor(F const& f) {
+      // TODO(5427828)
+#if 0
+      while (!f.isReady())
+        makeProgress();
+#else
+      while (!f.isReady()) {
+        run();
+      }
+#endif
+
+    }
+
+    virtual void scheduleAt(Func&& f, TimePoint const& t) override {
+      std::lock_guard<std::mutex> lock(lock_);
+      scheduledFuncs_.emplace(t, std::move(f));
+      sem_.post();
+    }
+
+    /// Advance the clock. The clock never advances on its own.
+    /// Advancing the clock causes some work to be done, if work is available
+    /// to do (perhaps newly available because of the advanced clock).
+    /// If dur is <= 0 this is a noop.
+    void advance(Duration const& dur) {
+      advanceTo(now_ + dur);
+    }
+
+    /// Advance the clock to this absolute time. If t is <= now(),
+    /// this is a noop.
+    void advanceTo(TimePoint const& t);
+
+    TimePoint now() override { return now_; }
+
+   private:
+    std::mutex lock_;
+    std::queue<Func> funcs_;
+    LifoSem sem_;
+
+    // helper class to enable ordering of scheduled events in the priority
+    // queue
+    struct ScheduledFunc {
+      TimePoint time;
+      size_t ordinal;
+      Func func;
+
+      ScheduledFunc(TimePoint const& t, Func&& f)
+        : time(t), func(std::move(f))
+      {
+        static size_t seq = 0;
+        ordinal = seq++;
+      }
+
+      bool operator<(ScheduledFunc const& b) const {
+        if (time == b.time)
+          return ordinal < b.ordinal;
+        return time < b.time;
+      }
+    };
+    std::priority_queue<ScheduledFunc> scheduledFuncs_;
+    TimePoint now_ = now_.min();
+  };
+
+}
diff --git a/faux-folly/folly/futures/OpaqueCallbackShunt.h b/faux-folly/folly/futures/OpaqueCallbackShunt.h
new file mode 100644
index 0000000..820cdb6
--- /dev/null
+++ b/faux-folly/folly/futures/OpaqueCallbackShunt.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 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/futures/Promise.h>
+
+namespace folly {
+
+/// These classes help you wrap an existing C style callback function
+/// into a Future.
+///
+///   void legacy_send_async(..., void (*cb)(void*), void*);
+///
+///   Future<T> wrappedSendAsync(T&& obj) {
+///     auto handle = new OpaqueCallbackShunt<T>(obj);
+///     auto future = handle->promise_.getFuture();
+///     legacy_send_async(..., OpaqueCallbackShunt<T>::callback, handle)
+///     return future;
+///   }
+///
+/// If the legacy function doesn't conform to void (*cb)(void*), use a lambda:
+///
+///   auto cb = [](t1*, t2*, void* arg) {
+///     OpaqueCallbackShunt<T>::callback(arg);
+///   };
+///   legacy_send_async(..., cb, handle);
+
+template <typename T>
+class OpaqueCallbackShunt {
+public:
+  explicit OpaqueCallbackShunt(T&& obj)
+    : obj_(std::move(obj)) { }
+  static void callback(void* arg) {
+    std::unique_ptr<OpaqueCallbackShunt<T>> handle(
+      static_cast<OpaqueCallbackShunt<T>*>(arg));
+    handle->promise_.setValue(std::move(handle->obj_));
+  }
+  folly::Promise<T> promise_;
+private:
+  T obj_;
+};
+
+} // folly
diff --git a/faux-folly/folly/futures/Promise-inl.h b/faux-folly/folly/futures/Promise-inl.h
new file mode 100644
index 0000000..751d9b4
--- /dev/null
+++ b/faux-folly/folly/futures/Promise-inl.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <thread>
+
+#include <folly/futures/FutureException.h>
+#include <folly/futures/detail/Core.h>
+
+namespace folly {
+
+template <class T>
+Promise<T>::Promise() : retrieved_(false), core_(new detail::Core<T>())
+{}
+
+template <class T>
+Promise<T>::Promise(Promise<T>&& other) noexcept
+    : retrieved_(other.retrieved_), core_(other.core_) {
+  other.core_ = nullptr;
+  other.retrieved_ = false;
+}
+
+template <class T>
+Promise<T>& Promise<T>::operator=(Promise<T>&& other) noexcept {
+  std::swap(core_, other.core_);
+  std::swap(retrieved_, other.retrieved_);
+  return *this;
+}
+
+template <class T>
+void Promise<T>::throwIfFulfilled() {
+  if (UNLIKELY(!core_)) {
+    throw NoState();
+  }
+  if (UNLIKELY(core_->ready())) {
+    throw PromiseAlreadySatisfied();
+  }
+}
+
+template <class T>
+void Promise<T>::throwIfRetrieved() {
+  if (UNLIKELY(retrieved_)) {
+    throw FutureAlreadyRetrieved();
+  }
+}
+
+template <class T>
+Promise<T>::~Promise() {
+  detach();
+}
+
+template <class T>
+void Promise<T>::detach() {
+  if (core_) {
+    if (!retrieved_)
+      core_->detachFuture();
+    core_->detachPromise();
+    core_ = nullptr;
+  }
+}
+
+template <class T>
+Future<T> Promise<T>::getFuture() {
+  throwIfRetrieved();
+  retrieved_ = true;
+  return Future<T>(core_);
+}
+
+template <class T>
+template <class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
+Promise<T>::setException(E const& e) {
+  setException(make_exception_wrapper<E>(e));
+}
+
+template <class T>
+void Promise<T>::setException(std::exception_ptr const& ep) {
+  try {
+    std::rethrow_exception(ep);
+  } catch (const std::exception& e) {
+    setException(exception_wrapper(std::current_exception(), e));
+  } catch (...) {
+    setException(exception_wrapper(std::current_exception()));
+  }
+}
+
+template <class T>
+void Promise<T>::setException(exception_wrapper ew) {
+  throwIfFulfilled();
+  core_->setResult(Try<T>(std::move(ew)));
+}
+
+template <class T>
+void Promise<T>::setInterruptHandler(
+  std::function<void(exception_wrapper const&)> fn) {
+  core_->setInterruptHandler(std::move(fn));
+}
+
+template <class T>
+void Promise<T>::setTry(Try<T>&& t) {
+  throwIfFulfilled();
+  core_->setResult(std::move(t));
+}
+
+template <class T>
+template <class M>
+void Promise<T>::setValue(M&& v) {
+  static_assert(!std::is_same<T, void>::value,
+                "Use setValue() instead");
+
+  setTry(Try<T>(std::forward<M>(v)));
+}
+
+template <class T>
+template <class F>
+void Promise<T>::setWith(F&& func) {
+  throwIfFulfilled();
+  setTry(makeTryWith(std::forward<F>(func)));
+}
+
+template <class T>
+bool Promise<T>::isFulfilled() {
+  if (core_) {
+    return core_->hasResult();
+  }
+  return true;
+}
+
+}
diff --git a/faux-folly/folly/futures/Promise.h b/faux-folly/folly/futures/Promise.h
new file mode 100644
index 0000000..eb67375
--- /dev/null
+++ b/faux-folly/folly/futures/Promise.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2015 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/futures/Try.h>
+#include <functional>
+
+namespace folly {
+
+// forward declaration
+template <class T> class Future;
+
+template <class T>
+class Promise {
+public:
+  Promise();
+  ~Promise();
+
+  // not copyable
+  Promise(Promise const&) = delete;
+  Promise& operator=(Promise const&) = delete;
+
+  // movable
+  Promise(Promise<T>&&) noexcept;
+  Promise& operator=(Promise<T>&&) noexcept;
+
+  /** Return a Future tied to the shared core state. This can be called only
+    once, thereafter Future already retrieved exception will be raised. */
+  Future<T> getFuture();
+
+  /** Fulfill the Promise with an exception_wrapper */
+  void setException(exception_wrapper ew);
+
+  /** Fulfill the Promise with an exception_ptr, e.g.
+    try {
+      ...
+    } catch (...) {
+      p.setException(std::current_exception());
+    }
+    */
+  FOLLY_DEPRECATED("use setException(exception_wrapper)")
+  void setException(std::exception_ptr const&);
+
+  /** Fulfill the Promise with an exception type E, which can be passed to
+    std::make_exception_ptr(). Useful for originating exceptions. If you
+    caught an exception the exception_wrapper form is more appropriate.
+    */
+  template <class E>
+  typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
+  setException(E const&);
+
+  /// Set an interrupt handler to handle interrupts. See the documentation for
+  /// Future::raise(). Your handler can do whatever it wants, but if you
+  /// bother to set one then you probably will want to fulfill the promise with
+  /// an exception (or special value) indicating how the interrupt was
+  /// handled.
+  void setInterruptHandler(std::function<void(exception_wrapper const&)>);
+
+  /// Sugar to fulfill this Promise<Unit>
+  template <class B = T>
+  typename std::enable_if<std::is_same<Unit, B>::value, void>::type
+  setValue() {
+    setTry(Try<T>(T()));
+  }
+
+  /** Set the value (use perfect forwarding for both move and copy) */
+  template <class M>
+  void setValue(M&& value);
+
+  void setTry(Try<T>&& t);
+
+  /** Fulfill this Promise with the result of a function that takes no
+    arguments and returns something implicitly convertible to T.
+    Captures exceptions. e.g.
+
+    p.setWith([] { do something that may throw; return a T; });
+  */
+  template <class F>
+  void setWith(F&& func);
+
+  bool isFulfilled();
+
+private:
+  typedef typename Future<T>::corePtr corePtr;
+  template <class> friend class Future;
+
+  // Whether the Future has been retrieved (a one-time operation).
+  bool retrieved_;
+
+  // shared core state object
+  corePtr core_;
+
+  void throwIfFulfilled();
+  void throwIfRetrieved();
+  void detach();
+};
+
+}
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise-inl.h>
diff --git a/faux-folly/folly/futures/QueuedImmediateExecutor.cpp b/faux-folly/folly/futures/QueuedImmediateExecutor.cpp
new file mode 100644
index 0000000..47b298f
--- /dev/null
+++ b/faux-folly/folly/futures/QueuedImmediateExecutor.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 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/futures/QueuedImmediateExecutor.h>
+#include <folly/ThreadLocal.h>
+#include <queue>
+
+namespace folly {
+
+void QueuedImmediateExecutor::addStatic(Func callback) {
+  static folly::ThreadLocal<std::queue<Func>> q_;
+
+  if (q_->empty()) {
+    q_->push(std::move(callback));
+    while (!q_->empty()) {
+      q_->front()();
+      q_->pop();
+    }
+  } else {
+    q_->push(callback);
+  }
+}
+
+} // namespace
diff --git a/faux-folly/folly/futures/QueuedImmediateExecutor.h b/faux-folly/folly/futures/QueuedImmediateExecutor.h
new file mode 100644
index 0000000..d1faa7e
--- /dev/null
+++ b/faux-folly/folly/futures/QueuedImmediateExecutor.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 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/Executor.h>
+
+namespace folly {
+
+/**
+ * Runs inline like InlineExecutor, but with a queue so that any tasks added
+ * to this executor by one of its own callbacks will be queued instead of
+ * executed inline (nested). This is usually better behavior than Inline.
+ */
+class QueuedImmediateExecutor : public Executor {
+ public:
+  /// There's really only one queue per thread, no matter how many
+  /// QueuedImmediateExecutor objects you may have.
+  static void addStatic(Func);
+
+  void add(Func func) override {
+    addStatic(std::move(func));
+  }
+};
+
+} // folly
diff --git a/faux-folly/folly/futures/README.md b/faux-folly/folly/futures/README.md
new file mode 100644
index 0000000..b1cca16
--- /dev/null
+++ b/faux-folly/folly/futures/README.md
@@ -0,0 +1,1079 @@
+<!-- This file is generated from the Dex guide by fbcode/folly/facebook/futures-update-readme.sh. -->
+<section class="dex_guide"><h1 class="dex_title">Futures</h1><section class="dex_document"><h1></h1><p class="dex_introduction">Futures is a framework for expressing asynchronous code in C++ using the Promise/Future pattern.</p><h2 id="overview">Overview <a href="#overview" class="headerLink">#</a></h2>
+
+<p>Folly Futures is an async C++ framework inspired by <a href="https://twitter.github.io/finagle/guide/Futures.html" target="_blank">Twitter&#039;s Futures</a> implementation in Scala (see also <a href="https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Future.scala" target="_blank">Future.scala</a>, <a href="https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Promise.scala" target="_blank">Promise.scala</a>, and friends), and loosely builds upon the existing but anemic Futures code found in the C++11 standard (<a href="http://en.cppreference.com/w/cpp/thread/future" target="_blank">std::future</a>) and <a href="http://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html#thread.synchronization.futures" target="_blank">boost::future</a> (especially &gt;= 1.53.0). 
+Although inspired by the C++11 std::future interface, it is not a drop-in replacement because some ideas don&#039;t translate well enough to maintain API compatibility.</p>
+
+<p>The primary difference from std::future is that you can attach callbacks to Futures (with <tt>then()</tt>), which enables sequential and parallel composition of Futures for cleaner asynchronous code.</p>
+
+<h2 id="brief-synopsis">Brief Synopsis <a href="#brief-synopsis" class="headerLink">#</a></h2>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="cp">#include &lt;folly/futures/Future.h&gt;</span>
+<span class="k">using</span> <span class="k">namespace</span> <span class="n">folly</span><span class="p">;</span>
+<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
+
+<span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
+  <span class="c1">// do something with x</span>
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;foo(&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;)&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
+<span class="p">}</span>
+
+<span class="c1">// ...</span>
+
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;making Promise&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
+  <span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+  <span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">foo</span><span class="p">);</span>
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Future chain made&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
+
+<span class="c1">// ... now perhaps in another event callback</span>
+
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;fulfilling Promise&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
+  <span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Promise fulfilled&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span></pre></div>
+
+<p>This would print:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">making</span> <span class="no">Promise</span>
+<span class="no">Future</span> <span class="no">chain</span> <span class="no">made</span>
+<span class="no">fulfilling</span> <span class="no">Promise</span>
+<span class="nf" data-symbol-name="foo">foo</span><span class="o">(</span><span class="mi">42</span><span class="o">)</span>
+<span class="no">Promise</span> <span class="no">fulfilled</span></pre></div>
+
+<h3 id="blog-post">Blog Post <a href="#blog-post" class="headerLink">#</a></h3>
+
+<p>In addition to this document, there is <a href="https://code.facebook.com/posts/1661982097368498/futures-for-c-11-at-facebook/" target="_blank">a blog post on code.facebook.com (June 2015)</a>.</p></section><section class="dex_document"><h1>Brief Guide</h1><p class="dex_introduction"></p><p>This brief guide covers the basics. For a more in-depth coverage skip to the appropriate section.</p>
+
+<p>Let&#039;s begin with an example using an imaginary simplified Memcache client interface:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">;</span>
+<span class="k">class</span> <span class="nc">MemcacheClient</span> <span class="p">{</span>
+ <span class="nl">public:</span>
+  <span class="k">struct</span> <span class="n">GetReply</span> <span class="p">{</span>
+    <span class="k">enum</span> <span class="k">class</span> <span class="nc">Result</span> <span class="p">{</span>
+      <span class="n">FOUND</span><span class="p">,</span>
+      <span class="n">NOT_FOUND</span><span class="p">,</span>
+      <span class="n">SERVER_ERROR</span><span class="p">,</span>
+    <span class="p">};</span>
+
+    <span class="n">Result</span> <span class="n">result</span><span class="p">;</span>
+    <span class="c1">// The value when result is FOUND,</span>
+    <span class="c1">// The error message when result is SERVER_ERROR or CLIENT_ERROR</span>
+    <span class="c1">// undefined otherwise</span>
+    <span class="n">string</span> <span class="n">value</span><span class="p">;</span>
+  <span class="p">};</span>
+
+  <span class="n">GetReply</span> <span class="nf">get</span><span class="p">(</span><span class="n">string</span> <span class="n">key</span><span class="p">);</span>
+<span class="p">};</span></pre></div>
+
+<p>This API is synchronous, i.e. when you call <tt>get()</tt> you have to wait for the result. This is very simple, but unfortunately it is also very easy to write very slow code using synchronous APIs.</p>
+
+<p>Now, consider this traditional asynchronous signature for <tt>get()</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="kt">int</span> <span class="nf">get</span><span class="p">(</span><span class="n">string</span> <span class="n">key</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="n">GetReply</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">callback</span><span class="p">);</span></pre></div>
+
+<p>When you call <tt>get()</tt>, your asynchronous operation begins and when it finishes your callback will be called with the result. Very performant code can be written with an API like this, but for nontrivial applications the code devolves into a special kind of spaghetti code affectionately referred to as &quot;callback hell&quot;.</p>
+
+<p>The Future-based API looks like this:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="n">GetReply</span><span class="o">&gt;</span> <span class="n">get</span><span class="p">(</span><span class="n">string</span> <span class="n">key</span><span class="p">);</span></pre></div>
+
+<p>A <tt>Future&lt;GetReply&gt;</tt> is a placeholder for the <tt>GetReply</tt> that we will eventually get. A Future usually starts life out &quot;unfulfilled&quot;, or incomplete, i.e.:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">fut</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">false</span>
+<span class="n">fut</span><span class="p">.</span><span class="n">value</span><span class="p">()</span>  <span class="c1">// will throw an exception because the Future is not ready</span></pre></div>
+
+<p>At some point in the future, the Future will have been fulfilled, and we can access its value.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">fut</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">true</span>
+<span class="n">GetReply</span><span class="o">&amp;</span> <span class="n">reply</span> <span class="o">=</span> <span class="n">fut</span><span class="p">.</span><span class="n">value</span><span class="p">();</span></pre></div>
+
+<p>Futures support exceptions. If something exceptional happened, your Future may represent an exception instead of a value. In that case:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">fut</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">true</span>
+<span class="n">fut</span><span class="p">.</span><span class="n">value</span><span class="p">()</span> <span class="c1">// will rethrow the exception</span></pre></div>
+
+<p>Just what is exceptional depends on the API. In our example we have chosen not to raise exceptions for <tt>SERVER_ERROR</tt>, but represent this explicitly in the <tt>GetReply</tt> object. On the other hand, an astute Memcache veteran would notice that we left <tt>CLIENT_ERROR</tt> out of <tt>GetReply::Result</tt>, and perhaps a <tt>CLIENT_ERROR</tt> would have been raised as an exception, because <tt>CLIENT_ERROR</tt> means there&#039;s a bug in the library and this would be truly exceptional. These decisions are judgement calls by the API designer. The important thing is that exceptional conditions (including and especially spurious exceptions that nobody expects) get captured and can be handled higher up the &quot;stack&quot;.</p>
+
+<p>So far we have described a way to initiate an asynchronous operation via an API that returns a Future, and then sometime later after it is fulfilled, we get its value. This is slightly more useful than a synchronous API, but it&#039;s not yet ideal. There are two more very important pieces to the puzzle.</p>
+
+<p>First, we can aggregate Futures, to define a new Future that completes after some or all of the aggregated Futures complete. Consider two examples: fetching a batch of requests and waiting for all of them, and fetching a group of requests and waiting for only one of them.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">vector</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">GetReply</span><span class="o">&gt;&gt;</span> <span class="n">futs</span><span class="p">;</span>
+<span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">key</span> <span class="o">:</span> <span class="n">keys</span><span class="p">)</span> <span class="p">{</span>
+  <span class="n">futs</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">mc</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">));</span>
+<span class="p">}</span>
+<span class="k">auto</span> <span class="n">all</span> <span class="o">=</span> <span class="n">collectAll</span><span class="p">(</span><span class="n">futs</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">futs</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
+
+<span class="n">vector</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">GetReply</span><span class="o">&gt;&gt;</span> <span class="n">futs</span><span class="p">;</span>
+<span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">key</span> <span class="o">:</span> <span class="n">keys</span><span class="p">)</span> <span class="p">{</span>
+  <span class="n">futs</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">mc</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">));</span>
+<span class="p">}</span>
+<span class="k">auto</span> <span class="n">any</span> <span class="o">=</span> <span class="n">collectAny</span><span class="p">(</span><span class="n">futs</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">futs</span><span class="p">.</span><span class="n">end</span><span class="p">());</span></pre></div>
+
+<p><tt>all</tt> and <tt>any</tt> are Futures (for the exact type and usage see the header files). They will be complete when all/one of futs are complete, respectively. (There is also <tt>collectN()</tt> for when you need some.)</p>
+
+<p>Second, we can attach callbacks to a Future, and chain them together monadically. An example will clarify:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="n">GetReply</span><span class="o">&gt;</span> <span class="n">fut1</span> <span class="o">=</span> <span class="n">mc</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;foo&quot;</span><span class="p">);</span>
+
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">fut2</span> <span class="o">=</span> <span class="n">fut1</span><span class="p">.</span><span class="n">then</span><span class="p">(</span>
+  <span class="p">[](</span><span class="n">GetReply</span> <span class="n">reply</span><span class="p">)</span> <span class="p">{</span>
+    <span class="k">if</span> <span class="p">(</span><span class="n">reply</span><span class="p">.</span><span class="n">result</span> <span class="o">==</span> <span class="n">MemcacheClient</span><span class="o">::</span><span class="n">GetReply</span><span class="o">::</span><span class="n">Result</span><span class="o">::</span><span class="n">FOUND</span><span class="p">)</span>
+      <span class="k">return</span> <span class="n">reply</span><span class="p">.</span><span class="n">value</span><span class="p">;</span>
+    <span class="k">throw</span> <span class="nf">SomeException</span><span class="p">(</span><span class="s">&quot;No value&quot;</span><span class="p">);</span>
+  <span class="p">});</span>
+
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">Unit</span><span class="o">&gt;</span> <span class="n">fut3</span> <span class="o">=</span> <span class="n">fut2</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="n">string</span> <span class="n">str</span><span class="p">)</span> <span class="p">{</span>
+    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">str</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="n">std</span><span class="o">::</span><span class="n">exception</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+    <span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
+  <span class="p">});</span></pre></div>
+
+<p>That example is a little contrived but the idea is that you can transform a result from one type to another, potentially in a chain, and unhandled errors propagate. Of course, the intermediate variables are optional.</p>
+
+<p>Using then to add callbacks is idiomatic. It brings all the code into one place, which avoids callback hell.</p>
+
+<p>Up to this point we have skirted around the matter of waiting for Futures. You may never need to wait for a Future, because your code is event-driven and all follow-up action happens in a then-block. But if want to have a batch workflow, where you initiate a batch of asynchronous operations and then wait for them all to finish at a synchronization point, then you will want to wait for a Future. Futures have a blocking method called <tt>wait()</tt> that does exactly that and optionally takes a timeout.</p>
+
+<p>Futures are partially threadsafe. A Promise or Future can migrate between threads as long as there&#039;s a full memory barrier of some sort. <tt>Future::then</tt> and <tt>Promise::setValue</tt> (and all variants that boil down to those two calls) can be called from different threads. <strong>But</strong>, be warned that you might be surprised about which thread your callback executes on. Let&#039;s consider an example.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// Thread A</span>
+<span class="n">Promise</span><span class="o">&lt;</span><span class="n">Unit</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+
+<span class="c1">// Thread B</span>
+<span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">z</span><span class="p">);</span>
+
+<span class="c1">// Thread A</span>
+<span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">();</span></pre></div>
+
+<p>This is legal and technically threadsafe. However, it is important to realize that you do not know in which thread <tt>x</tt>, <tt>y</tt>, and/or <tt>z</tt> will execute. Maybe they will execute in Thread A when <tt>p.setValue()</tt> is called. Or, maybe they will execute in Thread B when <tt>f.then</tt> is called. Or, maybe <tt>x</tt> will execute in Thread B, but <tt>y</tt> and/or <tt>z</tt> will execute in Thread A. There&#039;s a race between <tt>setValue</tt> and <tt>then</tt>&#x2014;whichever runs last will execute the callback. The only guarantee is that one of them will run the callback.</p>
+
+<p>Naturally, you will want some control over which thread executes callbacks. We have a few mechanisms to help.</p>
+
+<p>The first and most useful is <tt>via</tt>, which passes execution through an <tt>Executor</tt>, which usually has the effect of running the callback in a new thread.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">aFuture</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
+  <span class="p">.</span><span class="n">via</span><span class="p">(</span><span class="n">e1</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y1</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y2</span><span class="p">)</span>
+  <span class="p">.</span><span class="n">via</span><span class="p">(</span><span class="n">e2</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">z</span><span class="p">);</span></pre></div>
+
+<p><tt>x</tt> will execute in the current thread. <tt>y1</tt> and <tt>y2</tt> will execute in the thread on the other side of <tt>e1</tt>, and <tt>z</tt> will execute in the thread on the other side of <tt>e2</tt>. If after <tt>z</tt> you want to get back to the current thread, you need to get there via an executor. Another way to express this is using an overload of <tt>then</tt> that takes an Executor:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">aFuture</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">e1</span><span class="p">,</span> <span class="n">y1</span><span class="p">,</span> <span class="n">y2</span><span class="p">)</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">e2</span><span class="p">,</span> <span class="n">z</span><span class="p">);</span></pre></div>
+
+<p>Either way, there is no ambiguity about which thread will execute <tt>y1</tt>, <tt>y2</tt>, or <tt>z</tt>.</p>
+
+<p>You can still have a race after <tt>via</tt> if you break it into multiple statements, e.g. in this counterexample:</p>
+
+<div class="remarkup-code-block remarkup-counterexample" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">f</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">via</span><span class="p">(</span><span class="n">e1</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y1</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y2</span><span class="p">);</span> <span class="c1">// nothing racy here</span>
+<span class="n">f2</span><span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">y3</span><span class="p">);</span> <span class="c1">// racy</span></pre></div>
+
+<h2 id="you-make-me-promises-pro">You make me Promises, Promises <a href="#you-make-me-promises-pro" class="headerLink">#</a></h2>
+
+<p>If you are wrapping an asynchronous operation, or providing an asynchronous API to users, then you will want to make <tt>Promise</tt>s. Every Future has a corresponding Promise (except Futures that spring into existence already completed, with <tt>makeFuture()</tt>). Promises are simple: you make one, you extract the Future, and you fulfill it with a value or an exception. Example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+
+<span class="n">f</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">false</span>
+
+<span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
+
+<span class="n">f</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">true</span>
+<span class="n">f</span><span class="p">.</span><span class="n">value</span><span class="p">()</span> <span class="o">==</span> <span class="mi">42</span></pre></div>
+
+<p>and an exception example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+
+<span class="n">f</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">false</span>
+
+<span class="n">p</span><span class="p">.</span><span class="n">setException</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;Fail&quot;</span><span class="p">));</span>
+
+<span class="n">f</span><span class="p">.</span><span class="n">isReady</span><span class="p">()</span> <span class="o">==</span> <span class="nb">true</span>
+<span class="n">f</span><span class="p">.</span><span class="n">value</span><span class="p">()</span> <span class="c1">// throws the exception</span></pre></div>
+
+<p>It&#039;s good practice to use setWith which takes a function and automatically captures exceptions, e.g.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="n">p</span><span class="p">.</span><span class="n">setWith</span><span class="p">([]{</span>
+  <span class="n">try</span> <span class="p">{</span>
+    <span class="c1">// do stuff that may throw</span>
+    <span class="k">return</span> <span class="mi">42</span><span class="p">;</span>
+  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">MySpecialException</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// handle it</span>
+    <span class="k">return</span> <span class="mi">7</span><span class="p">;</span>
+  <span class="p">}</span>
+  <span class="c1">// Any exceptions that we didn&#39;t catch, will be caught for us</span>
+<span class="p">});</span></pre></div></section><section class="dex_document"><h1>More Details</h1><p class="dex_introduction"></p><p>Let&#039;s look at a contrived and synchronous example of Futures.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+<span class="c1">// ...</span>
+<span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span> <span class="c1">// or setException(...)</span>
+<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">f</span><span class="p">.</span><span class="n">value</span><span class="p">();</span> <span class="c1">// prints 42</span></pre></div>
+
+<p>First, we create a <a href="https://github.com/facebook/folly/blob/master/folly/futures/Promise.h" target="_blank">Promise</a> object of type <tt>int</tt>. This object is exactly what it sounds like&#x2014;a pledge to provide an <tt>int</tt> (or an exception) at some point in the future.</p>
+
+<p>Next, we extract a <a href="https://github.com/facebook/folly/blob/master/folly/futures/Future.h" target="_blank">Future</a> object from that promise. You can think of futures as handles on promises - they provide a way to access that <tt>int</tt> when the promise is fulfilled.</p>
+
+<p>Later, when the promise is fulfilled via <tt>setValue()</tt> or <tt>setException()</tt>, that <tt>int</tt> is accessible via the future&#039;s <tt>value()</tt> method. That method will throw if the future contains an exception.</p>
+
+<h2 id="setting-callbacks-with-t">Setting callbacks with then() <a href="#setting-callbacks-with-t" class="headerLink">#</a></h2>
+
+<p>Ok, great, so now you&#039;re wondering what these are actually useful for. Let&#039;s consider another way to follow up on the result of a <tt>Future</tt> once its corresponding <tt>Promise</tt> is fulfilled&#x2014;callbacks! Here&#039;s a snippet that is functionally equivalent to the one above:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+
+<span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">int</span> <span class="n">i</span><span class="p">){</span>
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">;</span>
+<span class="p">});</span>
+
+<span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span></pre></div>
+
+<p>That <tt>then()</tt> method on futures is the real bread and butter of Futures code. It allows you to provide a callback which will be executed when that <tt>Future</tt> is complete. Note that while we fulfill the promise after setting the callback here, those operations could be swapped. Setting a callback on an already completed future executes the callback immediately.</p>
+
+<p>In this case, the callback takes a value directly. If the Future contained an exception, the callback will be passed over and the exception will be propagated to the resultant Future - more on that in a second. Your callback may also take a <a href="https://github.com/facebook/folly/blob/master/folly/futures/Try.h" target="_blank">Try</a>, which encapsulates either an exception or a value of its templated type.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">){</span>
+  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">t</span><span class="p">.</span><span class="n">value</span><span class="p">();</span>
+<span class="p">});</span></pre></div>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> Do not use Try unless you are actually going to do exception handling in your callback. It is much cleaner and often more performant to take the value directly when you can. If you want to do exception handling, there still might be better options than Try. See <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14604">Error Handling</a>.</div>
+
+<p>The real power of <tt>then()</tt> is that it <em>returns a <tt>Future</tt> of the type that the callback returns</em> and can therefore be chained and nested with ease. Let&#039;s consider another example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">f2</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">int</span> <span class="n">i</span><span class="p">){</span>
+  <span class="k">return</span> <span class="n">folly</span><span class="o">::</span><span class="n">to</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
+<span class="p">});</span>
+
+<span class="n">f2</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="n">string</span> <span class="n">s</span><span class="p">){</span> <span class="cm">/* ... */</span> <span class="p">});</span></pre></div>
+
+<p>Here, we convert that <tt>int</tt> to a <tt>string</tt> in the callback and return the result, which results in a <tt>Future&lt;string&gt;</tt> that we can set further callbacks on. I&#039;ve created a named variable <tt>f2</tt> to demonstrate types but don&#039;t hesitate to chain futures directly:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">finalFuture</span> <span class="o">=</span> <span class="n">getSomeFuture</span><span class="p">()</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(...)</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(...)</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(...);</span></pre></div>
+
+<p>That&#039;s all great, but this code is still synchronous. These constructs truly become useful when you start to chain, nest, and compose asynchronous operations. Let&#039;s say you instead have some <em>remote</em> service that converts your integers to strings for you, and that you also have a client with Future interfaces (i.e. interfaces that return Futures). Now let&#039;s leverage the fact that <tt>then()</tt> also allows you to return <tt>Future&lt;T&gt;</tt> from inside your callbacks as well as just <tt>T</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">f2</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">int</span> <span class="n">i</span><span class="p">){</span>
+  <span class="k">return</span> <span class="n">getClient</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">future_intToString</span><span class="p">(</span><span class="n">i</span><span class="p">);</span> <span class="c1">// returns Future&lt;string&gt;</span>
+<span class="p">});</span>
+
+<span class="n">f2</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="n">Try</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">){</span> <span class="p">...</span> <span class="p">});</span></pre></div>
+
+<p>In general, your code will be cleaner if you return <tt>T</tt> from your callbacks and only switch to returning <tt>Future&lt;T&gt;</tt> when necessary (i.e. when there is a nested call to a future-returning function).</p>
+
+<h2 id="futures-promises-and-mov">Futures, Promises, and move semantics <a href="#futures-promises-and-mov" class="headerLink">#</a></h2>
+
+<p><tt>Futures</tt> and <tt>Promises</tt> are movable but non-copyable. This preserves the invariant of a one-to-one mapping between a Promise and a Future and as a side effect encourages performant code. There is a piece of heap-allocated shared state underlying each promise-future pair&#x2014;keep this in mind as a bare minimum performance cost.</p>
+
+<h2 id="synchronously-creating-a">Synchronously creating and completing futures <a href="#synchronously-creating-a" class="headerLink">#</a></h2>
+
+<p>Synchronously entering and exiting the futures paradigm can be useful, especially in tests, so the following utilities are available:</p>
+
+<ul>
+<li>Create already-completed futures with <tt>makeFuture&lt;T&gt;()</tt>, which takes a <tt>T&amp;&amp;</tt> (or an exception, more info <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14604">here</a>). If you pass <tt>T&amp;&amp;</tt> the type is inferred and you don&#039;t have to specify it.</li>
+<li>Extract a future&#039;s <tt>T</tt> value with <tt>Future&lt;T&gt;::get()</tt>. This method is blocking, so make sure that either your future is already completed or that another thread will complete the future while the calling thread blocks. <tt>get()</tt> can also take a timeout&#x2014;see <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14677">Timeouts</a>.</li>
+<li>Perform a blocking wait on a Future with <tt>Future&lt;T&gt;::wait()</tt>. This is just like <tt>get()</tt> but it instead of extracting the value or throwing the exception, <tt>wait()</tt> returns a new Future with the result of the input Future. Like <tt>get()</tt>, <tt>wait()</tt> can also take a timeout&#x2014;see <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14677">Timeouts</a>.</li>
+<li><tt>getVia()</tt> and <tt>waitVia()</tt>, which are like <tt>get()</tt> and <tt>wait()</tt> except that they drive some Executor (say, an <tt>EventBase</tt>) until the Future is complete. See <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14606">Testing</a> for more.</li>
+</ul>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> <tt>makeFuture()</tt>, <tt>get()</tt>, <tt>wait()</tt>, and friends are especially handy in tests and are documented further in the <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14606">Testing</a> article.</div>
+
+<h2 id="overloads-of-then">Overloads of then() <a href="#overloads-of-then" class="headerLink">#</a></h2>
+
+<p>Above are demonstrations of variants of <tt>then()</tt> whose callbacks</p>
+
+<ul>
+<li>return <tt>Future&lt;T&gt;</tt> or <tt>T</tt></li>
+<li>take <tt>T const&amp;</tt> or <tt>Try&lt;T&gt; const&amp;</tt> (also possible are <tt>T</tt>, <tt>Try&lt;T&gt;</tt>, <tt>T&amp;&amp;</tt>, and <tt>Try&lt;T&gt;&amp;&amp;</tt>)</li>
+</ul>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> The preferred pattern is&#x2014;when possible&#x2014;to use value semantics (take a <tt>T</tt> or <tt>Try&lt;T&gt;</tt>). If your type is expensive to copy or can&#039;t be copied, take a reference. (e.g. <tt>T const&amp;</tt> or <tt>Try&lt;T&gt; const&amp;</tt>) If you need move semantics, an lvalue reference or rvalue reference is the same in this situation. Use whichever you stylistically prefer.</div>
+
+<p>The flexibility doesn&#039;t end there. There are also overloads so that you can bind global functions, member functions, and static member functions to <tt>then()</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="kt">void</span> <span class="nf">globalFunction</span><span class="p">(</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">);</span>
+
+<span class="k">struct</span> <span class="n">Foo</span> <span class="p">{</span>
+  <span class="kt">void</span> <span class="n">memberMethod</span><span class="p">(</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">);</span>
+  <span class="k">static</span> <span class="kt">void</span> <span class="nf">staticMemberMethod</span><span class="p">(</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="k">const</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">);</span>
+<span class="p">};</span>
+<span class="n">Foo</span> <span class="n">foo</span><span class="p">;</span>
+
+<span class="c1">// bind global function</span>
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">globalFunction</span><span class="p">);</span>
+<span class="c1">// bind member method</span>
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Foo</span><span class="o">::</span><span class="n">memberMethod</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">foo</span><span class="p">);</span>
+<span class="c1">// bind static member method</span>
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">3</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Foo</span><span class="o">::</span><span class="n">staticMemberMethod</span><span class="p">);</span></pre></div>
+
+<h2 id="a-note-on-promises">A note on Promises <a href="#a-note-on-promises" class="headerLink">#</a></h2>
+
+<p>Generally speaking, the majority of your futures-based code will deal with <tt>Futures</tt> alone and not <tt>Promises</tt>&#x2014;calling <tt>Future</tt>-returning interfaces, composing callbacks on them, and eventually returning another <tt>Future</tt>. <tt>Promises</tt> are most useful when you&#039;re wrapping some lower level asynchronous interface so that you can return a <tt>Future</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="kt">void</span> <span class="nf">fooOldFashioned</span><span class="p">(</span><span class="kt">int</span> <span class="n">arg</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">callback</span><span class="p">);</span>
+
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
+  <span class="k">auto</span> <span class="n">promise</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">make_shared</span><span class="o">&lt;</span><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span><span class="p">();</span>
+
+  <span class="n">fooOldFashioned</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="p">[</span><span class="n">promise</span><span class="p">](</span><span class="kt">int</span> <span class="n">result</span><span class="p">)</span> <span class="p">{</span>
+    <span class="n">promise</span><span class="o">-&gt;</span><span class="n">setValue</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
+  <span class="p">});</span>
+
+  <span class="k">return</span> <span class="n">promise</span><span class="o">-&gt;</span><span class="n">getFuture</span><span class="p">();</span>
+<span class="p">}</span></pre></div>
+
+<p>Though not a hard-and-fast rule, using promises heavily in your code might indicate</p>
+
+<ul>
+<li>an opportunity for a cleaner futures-based version</li>
+<li>a missing abstraction in our library. See <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14607">Compositional Building Blocks</a>, <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14677">Timeouts</a>, <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14616">Interrupts</a>, etc. Let us know if you think this is the case.</li>
+</ul>
+
+<h2 id="sharedpromise">SharedPromise <a href="#sharedpromise" class="headerLink">#</a></h2>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/futures/SharedPromise.h" target="_blank">SharedPromise</a> provides the same interface as Promise, but you can extract multiple Futures from it, i.e. you can call <tt>getFuture()</tt> as many times as you&#039;d like. When the SharedPromise is fulfilled, all of the Futures will be called back. Calls to getFuture() after the SharedPromise is fulfilled return a completed Future. If you find yourself constructing collections of Promises and fulfilling them simultaneously with the same value, consider this utility instead. Likewise, if you find yourself in need of setting multiple callbacks on the same Future (which is indefinitely unsupported), consider refactoring to use SharedPromise to &quot;split&quot; the Future.</p></section><section class="dex_document"><h1>Error Handling</h1><p class="dex_introduction">Asynchronous code can't employ try/catch exception handling universally, so Futures provides facilities to make error handling as easy and natural as possible. Here's an overview.</p><h2 id="throwing-exceptions">Throwing Exceptions <a href="#throwing-exceptions" class="headerLink">#</a></h2>
+
+<p>There are several ways to introduce exceptions into your Futures flow. First, <tt>makeFuture&lt;T&gt;()</tt> and <tt>Promise&lt;T&gt;::setException()</tt> can create a failed future from any <tt>std::exception</tt>, from a <tt>folly::exception_wrapper</tt>, or from an <tt>std::exception_ptr</tt> (deprecated):</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;oh no!&quot;</span><span class="p">));</span>
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">folly</span><span class="o">::</span><span class="n">make_exception_wrapper</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="o">&gt;</span><span class="p">(</span><span class="s">&quot;oh no!&quot;</span><span class="p">));</span>
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">current_exception</span><span class="p">());</span>
+
+<span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">p3</span><span class="p">;</span>
+<span class="n">p1</span><span class="p">.</span><span class="n">setException</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;oh no!&quot;</span><span class="p">));</span>
+<span class="n">p2</span><span class="p">.</span><span class="n">setException</span><span class="p">(</span><span class="n">folly</span><span class="o">::</span><span class="n">make_exception_wrapper</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="o">&gt;</span><span class="p">(</span><span class="s">&quot;oh no!&quot;</span><span class="p">));</span>
+<span class="n">p3</span><span class="p">.</span><span class="n">setException</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">current_exception</span><span class="p">());</span></pre></div>
+
+<p>In general, any time you pass a function to a method that returns a <tt>Future</tt> or fulfills a <tt>Promise</tt>, you can rest assured that any thrown exceptions (including non-<tt>std::exceptions</tt>) will be caught and stored. For instance,</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">makeFuture</span><span class="p">().</span><span class="n">then</span><span class="p">([]{</span>
+  <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;ugh&quot;</span><span class="p">);</span>
+<span class="p">});</span></pre></div>
+
+<p>is perfectly valid code. The exception will be caught and stored in the resultant <tt>Future</tt>.</p>
+
+<p>Methods that behave this way include</p>
+
+<ul>
+<li><tt>Future&lt;T&gt;::then()</tt> and all its variants</li>
+<li><tt>Future&lt;T&gt;::onError()</tt>: more on this below</li>
+<li><tt>makeFutureTry()</tt>: takes a function, executes it, and creates a Future with the result or any thrown exception</li>
+<li><tt>Promise&lt;T&gt;::setWith()</tt>: similar to <tt>makeFutureTry</tt> except it fulfills a Promise instead of creating a completed Future</li>
+</ul>
+
+<h2 id="catching-exceptions">Catching Exceptions <a href="#catching-exceptions" class="headerLink">#</a></h2>
+
+<p>There are also several ways to handle exceptions in Futures code.</p>
+
+<h3 id="using-try">Using Try <a href="#using-try" class="headerLink">#</a></h3>
+
+<p>First, there&#039;s the <tt>Try</tt> abstraction which multiplexes values and exceptions so they can be handled simultaneously in a <tt>then()</tt> callback:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;ugh&quot;</span><span class="p">)).</span><span class="n">then</span><span class="p">([](</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">t</span><span class="p">){</span>
+  <span class="n">try</span> <span class="p">{</span>
+    <span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="n">t</span><span class="p">.</span><span class="n">value</span><span class="p">();</span> <span class="c1">// will rethrow</span>
+    <span class="c1">// handle success</span>
+  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// handle failure</span>
+  <span class="p">}</span>
+<span class="p">});</span>
+
+<span class="c1">// Try is also integrated with exception_wrapper</span>
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;ugh&quot;</span><span class="p">)).</span><span class="n">then</span><span class="p">([](</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">t</span><span class="p">){</span>
+  <span class="k">if</span> <span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">hasException</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&gt;</span><span class="p">())</span> <span class="p">{</span>
+    <span class="c1">// this is enough if we only care whether the given exception is present</span>
+  <span class="p">}</span>
+<span class="p">});</span>
+
+<span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;ugh&quot;</span><span class="p">)).</span><span class="n">then</span><span class="p">([](</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">t</span><span class="p">){</span>
+  <span class="c1">// we can also extract and handle the exception object</span>
+  <span class="c1">// TODO(jsedgwick) infer exception type from the type of the function</span>
+  <span class="kt">bool</span> <span class="n">caught</span> <span class="o">=</span> <span class="n">t</span><span class="p">.</span><span class="n">withException</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&gt;</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">){</span>
+    <span class="c1">// do something with e</span>
+  <span class="p">});</span>
+<span class="p">});</span></pre></div>
+
+<p>Unfortunately, <tt>Try</tt> encourages both intertwining success and error logic as well as excessive rethrowing. Thankfully, there&#039;s another option.</p>
+
+<h3 id="using-onerror">Using onError() <a href="#using-onerror" class="headerLink">#</a></h3>
+
+<p><tt>Future&lt;T&gt;::onError()</tt> allows you to have individual exception handlers as separate callbacks. The parameter you specify for your callback is exactly what <tt>onError()</tt> will try to catch. The callback will be passed over if the future doesn&#039;t contain that exception, otherwise, it will be executed and the T or Future&lt;T&gt; that it returns will become the resultant Future instead.</p>
+
+<div class="remarkup-warning"><span class="remarkup-note-word">WARNING:</span> Chaining together multiple calls to onError will NOT necessarily behave in the same way as multiple catch &#123;&#125; blocks after a try. Namely, if you throw an exception in one call to onError, the next onError will catch it.</div>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">intGeneratorThatMaybeThrows</span><span class="p">()</span> <span class="c1">// returns Future&lt;int&gt;</span>
+  <span class="c1">// This is a good opportunity to use the plain value (no Try)</span>
+  <span class="c1">// variant of then()</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> 
+    <span class="k">return</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// maybe we throw here instead</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// ... runtime_error handling ...</span>
+    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// ... all other exception handling ...</span>
+    <span class="k">return</span> <span class="o">-</span><span class="mi">2</span><span class="p">;</span>
+  <span class="p">});</span></pre></div>
+
+<p>You can also use <tt>onError()</tt> directly with <tt>exception_wrapper</tt>. One use case for this variant is if you want to handle non-<tt>std::exception</tt> exceptions.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">makeFuture</span><span class="p">().</span><span class="n">then</span><span class="p">([]{</span>
+  <span class="k">throw</span> <span class="mi">42</span><span class="p">;</span>
+<span class="p">})</span>
+<span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="n">exception_wrapper</span> <span class="n">ew</span><span class="p">){</span>
+  <span class="c1">// ...</span>
+<span class="p">});</span></pre></div>
+
+<h2 id="ensure">ensure() <a href="#ensure" class="headerLink">#</a></h2>
+
+<p><tt>Future&lt;T&gt;::ensure(F func)</tt> is similar to the <tt>finally</tt> block in languages like Java. That is, it takes a void function and will execute regardless of whether the Future contains a value or an exception. The resultant Future will contain the exception/value of the original Future, unless the function provided to ensure throws, in which case that exception will be caught and propagated instead. For instance:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(...);</span>
+<span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">makeFuture</span><span class="p">().</span><span class="n">then</span><span class="p">([</span><span class="n">fd</span><span class="p">]{</span>
+  <span class="c1">// do some stuff with the file descriptor</span>
+  <span class="c1">// maybe we throw, maybe we don&#39;t</span>
+<span class="p">})</span>
+<span class="p">.</span><span class="n">ensure</span><span class="p">([</span><span class="n">fd</span><span class="p">]{</span>
+  <span class="c1">// either way, let&#39;s release that fd</span>
+  <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
+<span class="p">});</span>
+
+<span class="c1">// f now contains the result of the then() callback, unless the ensure()</span>
+<span class="c1">// callback threw, in which case f will contain that exception</span></pre></div>
+
+<h2 id="performant-exception-han">Performant Exception Handling <a href="#performant-exception-han" class="headerLink">#</a></h2>
+
+<p>Under the hood, the Futures use <tt>folly::exception_wrapper</tt> to store exceptions in a way that minimizes costly rethrows. However, the effectiveness of this mechanism depends on whether exceptions are supplied in a way that enables our library (and <tt>exception_wrapper</tt>) to maintain type information about your exception. Practically speaking, this means constructing exceptional futures directly instead of throwing. For instance:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// This version will throw the exception twice</span>
+<span class="n">makeFuture</span><span class="p">()</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">([]{</span>
+    <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;ugh&quot;</span><span class="p">);</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">){</span>
+    <span class="c1">// ...</span>
+  <span class="p">});</span>
+<span class="c1">// This version won&#39;t throw at all!</span>
+<span class="n">makeFuture</span><span class="p">()</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">([]{</span>
+    <span class="c1">// This will properly wrap the exception</span>
+    <span class="k">return</span> <span class="n">makeFuture</span><span class="o">&lt;</span><span class="n">Unit</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;ugh&quot;</span><span class="p">));</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">){</span>
+    <span class="c1">// ...</span>
+  <span class="p">});</span></pre></div>
+
+<p>Likewise, using <tt>onError</tt> instead of throwing via <tt>Try</tt> will often reduce rethrows. If you want to use <tt>Try</tt>, look at <tt>Try&lt;T&gt;::hasException()</tt> and <tt>Try&lt;T&gt;::withException()</tt> for ways to inspect and handle exceptions without rethrows.</p>
+
+<p>Be wary of premature optimization, and err towards clean code over minimizing rethrows unless you&#039;re sure you need the performance. That said, we will continue to strive to make the cleanest option the most performant one as well.</p></section><section class="dex_document"><h1>Compositional Building Blocks</h1><p class="dex_introduction">Sometimes chaining and nesting with then() is not enough. Here are some utilities for composing futures.</p><div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> For maximum flexibility, many of the helpers documented below take start and end iterators on a collection. All such functions have overloads that take just the collection by reference and automatically operate on the <tt>begin()</tt> and <tt>end()</tt> iterators. You will almost always want to take advantage of this sugar. For instance, <tt>collect(futures.begin(), futures.end())</tt> can be written as simply  <tt>collect(futures)</tt>.</div>
+
+<h2 id="collectall">collectAll() <a href="#collectall" class="headerLink">#</a></h2>
+
+<p><tt>collectAll()</tt> takes an iterable collection of <tt>Future&lt;T&gt;</tt>s (or start and end iterators on such a collection) and returns a <tt>Future&lt;std::vector&lt;Try&lt;T&gt;&gt;&gt;</tt> that will complete once all of the input futures complete. The resultant Future&#039;s vector will contain the results of each in the same order in which they were passed. Errors in any component Future will not cause early termination. Input Futures are moved in and are no longer valid. For example, we could fan out and fan in a bunch of RPCs like so:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">someRPC</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">);</span>
+
+<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span> <span class="n">fs</span><span class="p">;</span>
+<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
+  <span class="n">fs</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">someRPC</span><span class="p">(</span><span class="n">i</span><span class="p">));</span>
+<span class="p">}</span>
+
+<span class="n">collectAll</span><span class="p">(</span><span class="n">fs</span><span class="p">).</span><span class="n">then</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Try</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&amp;</span> <span class="n">tries</span><span class="p">){</span>
+  <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">t</span> <span class="o">:</span> <span class="n">tries</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// handle each response</span>
+  <span class="p">}</span>
+<span class="p">});</span></pre></div>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> Just as with any then() callback, you could take a Try instead and it would compile. But you shouldn&#039;t, because the only way the outer Future can fail is if there&#039;s a bug in our library. Save yourself some typing and skip the Try. This advice also applies to all of the compositional operations below whose Future types contain inner Trys (i.e. everything except for <tt>collect()</tt> and <tt>map()</tt>).</div>
+
+<h2 id="collectall-variadic">collectAll() variadic <a href="#collectall-variadic" class="headerLink">#</a></h2>
+
+<p>There is also a variadically templated flavor of <tt>collectAll()</tt> that allows you to mix and match different types of Futures. It returns a <tt>Future&lt;std::tuple&lt;Try&lt;T1&gt;, Try&lt;T2&gt;, ...&gt;&gt;</tt>. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f1</span> <span class="o">=</span> <span class="p">...;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">f2</span> <span class="o">=</span> <span class="p">...;</span>
+<span class="n">collectAll</span><span class="p">(</span><span class="n">f1</span><span class="p">,</span> <span class="n">f2</span><span class="p">).</span><span class="n">then</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;</span><span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">Try</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;&gt;&amp;</span> <span class="n">tup</span><span class="p">)</span> <span class="p">{</span>
+  <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">&gt;</span><span class="p">(</span><span class="n">tup</span><span class="p">).</span><span class="n">value</span><span class="p">();</span>
+  <span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">1</span><span class="o">&gt;</span><span class="p">(</span><span class="n">tup</span><span class="p">).</span><span class="n">value</span><span class="p">();</span>
+  <span class="c1">// ...</span>
+<span class="p">});</span></pre></div>
+
+<h2 id="collect">collect() <a href="#collect" class="headerLink">#</a></h2>
+
+<p><tt>collect()</tt> is similar to <tt>collectAll()</tt>, but will terminate early if an exception is raised by any of the input Futures. Therefore, the returned Future is of type <tt>std::vector&lt;T&gt;</tt>. Like <tt>collectAll()</tt>, input Futures are moved in and are no longer valid, and the resulting Future&#039;s vector will contain the results of each input Future in the same order they were passed in (if all are successful). For instance:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">collect</span><span class="p">(</span><span class="n">fs</span><span class="p">).</span><span class="n">then</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&amp;</span> <span class="n">vals</span><span class="p">)</span> <span class="p">{</span>
+  <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">val</span> <span class="o">:</span> <span class="n">vals</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// handle each response</span>
+  <span class="p">}</span>
+<span class="p">})</span>
+<span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+  <span class="c1">// drat, one of them failed</span>
+<span class="p">});</span>
+
+<span class="c1">// Or using a Try:</span>
+<span class="n">collect</span><span class="p">(</span><span class="n">fs</span><span class="p">).</span><span class="n">then</span><span class="p">([](</span><span class="k">const</span> <span class="n">Try</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&amp;</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span>
+ <span class="c1">// ...</span>
+<span class="p">});</span></pre></div>
+
+<h2 id="collect-variadic">collect() variadic <a href="#collect-variadic" class="headerLink">#</a></h2>
+
+<p>There is also a variadically templated flavor of <tt>collect()</tt> that allows you to mix and match different types of Futures. It returns a <tt>Future&lt;std::tuple&lt;T1, T2, ...&gt;&gt;</tt>.</p>
+
+<h2 id="collectn">collectN() <a href="#collectn" class="headerLink">#</a></h2>
+
+<p><tt>collectN</tt>, like <tt>collectAll()</tt>, takes a collection of Futures, or a pair of iterators thereof, but it also takes a <tt>size_t</tt> N and will complete once N of the input futures are complete. It returns a <tt>Future&lt;std::vector&lt;std::pair&lt;size_t, Try&lt;T&gt;&gt;&gt;&gt;</tt>. Each pair holds the index of the corresponding Future in the original collection as well as its result, though the pairs themselves will be in arbitrary order. Like <tt>collectAll()</tt>, <tt>collectN()</tt> moves in the input Futures, so your copies are no longer valid. If multiple input futures complete &quot;simultaneously&quot; or are already completed, winners are chosen but the choice is undefined.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// Wait for 5 of the input futures to complete</span>
+<span class="n">collectN</span><span class="p">(</span><span class="n">fs</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span>
+  <span class="p">[](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">size_t</span><span class="p">,</span> <span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;&gt;&amp;</span> <span class="n">tries</span><span class="p">){</span>
+    <span class="c1">// there will be 5 pairs</span>
+    <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">pair</span> <span class="o">:</span> <span class="n">tries</span><span class="p">)</span> <span class="p">{</span>
+      <span class="kt">size_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">pair</span><span class="p">.</span><span class="n">first</span><span class="p">;</span>
+      <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">pair</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">value</span><span class="p">();</span>
+      <span class="c1">// ...</span>
+    <span class="p">}</span>
+  <span class="p">});</span></pre></div>
+
+<h2 id="collectany">collectAny() <a href="#collectany" class="headerLink">#</a></h2>
+
+<p><tt>collectAny()</tt> also takes a collection of Futures (or a pair of iterators thereof), but it completes as soon as any of the input Futures completes. It returns a <tt>Future&lt;std::pair&lt;size_t, Try&lt;T&gt;&gt;&gt;</tt> which holds the index of the first completed Future along with its result. The input futures are moved in, so your copies are no longer valid. If multiple input futures complete &quot;simultaneously&quot; or are already completed, a winner is chosen but the choice is undefined. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">collectAny</span><span class="p">(</span><span class="n">fs</span><span class="p">,</span> <span class="p">[](</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">size_t</span><span class="p">,</span> <span class="n">Try</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;&amp;</span> <span class="n">p</span><span class="p">){</span>
+  <span class="kt">size_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span><span class="p">;</span>
+  <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">value</span><span class="p">();</span>
+  <span class="c1">// ...</span>
+<span class="p">});</span></pre></div>
+
+<h2 id="map">map() <a href="#map" class="headerLink">#</a></h2>
+
+<p><tt>map()</tt> is the Futures equivalent of the higher order function <a href="http://en.wikipedia.org/wiki/Map_%28higher-order_function%29" target="_blank">map</a>. It takes a collection of <tt>Future&lt;A&gt;</tt> (or a pair of iterators thereof) and a function that can be passed to Future&lt;A&gt;::then(), and in turn calls then() with the function on each input Future. It returns a vector of the resultant Futures in the order they were passed in. This is simple sugar for:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;&gt;</span> <span class="n">fs</span><span class="p">;</span>
+<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;&gt;</span> <span class="n">fs2</span><span class="p">;</span>
+<span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">fs</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">it</span> <span class="o">&lt;</span> <span class="n">fs</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
+  <span class="n">fs2</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">it</span><span class="o">-&gt;</span><span class="n">then</span><span class="p">(</span><span class="n">func</span><span class="p">));</span>
+<span class="p">}</span></pre></div>
+
+<p>For instance, say you have some expensive RPC that fetches an <tt>int</tt> and you&#039;d like to do expensive processing on each of many calls to this RPC. <tt>collect()</tt> or <tt>collectAll()</tt> might not be wise since they wait for all the results to be ready, while you&#039;d rather process the integers as they arrive. You could use <tt>map()</tt> in this scenario:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">fs2</span> <span class="o">=</span> <span class="n">map</span><span class="p">(</span><span class="n">fs</span><span class="p">,</span> <span class="n">expensiveProcessingFunc</span><span class="p">);</span>
+<span class="c1">// You probably now want to wait for all of these to complete. Call</span>
+<span class="c1">// collect() or collectAll() on fs2 to obtain such a Future.</span></pre></div>
+
+<h2 id="reduce">reduce() <a href="#reduce" class="headerLink">#</a></h2>
+
+<p><tt>reduce()</tt> is the Futures equivalent of the higher order function <a href="http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29" target="_blank">fold</a> (foldl, specifically). It takes a collection of <tt>Future&lt;A&gt;</tt> (or a pair of iterators thereof), an initial value of type <tt>B</tt>, and a function taking two arguments - the reduced value of type <tt>B</tt> and the next result from the collection of <tt>Future&lt;A&gt;</tt>. The function must return either <tt>B</tt> or <tt>Future&lt;B&gt;</tt>. <tt>reduce()</tt>, in turn, returns a <tt>Future&lt;B&gt;</tt>. The function will be applied to the initial value and the result of the first Future, and then to the result of that initial application and the result of the second Future, and so on until the whole collection of Futures has been reduced or an unhandled exception is hit.</p>
+
+<p>The second argument to the reducing function can be either <tt>A</tt> or <tt>Try&lt;A&gt;</tt>, depending on whether you want to handle exceptions from the input Futures. If there is an exception in an input Future and you don&#039;t take a <tt>Try</tt>, the reduce operation will short circuit with that exception. Any exception thrown in the reducing function will similarly short circuit the whole operation.</p>
+
+<p>For instance, if you have a collection of <tt>Future&lt;int&gt;</tt> and you want a <tt>Future&lt;bool&gt;</tt> that contains true if and only if all the input <tt>ints</tt> are equal to zero, you might write:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">reduce</span><span class="p">(</span><span class="n">fs</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="p">[](</span><span class="kt">bool</span> <span class="n">b</span><span class="p">,</span> <span class="kt">int</span> <span class="n">i</span><span class="p">){</span>
+  <span class="c1">// You could also return a Future&lt;bool&gt; if you needed to</span>
+  <span class="k">return</span> <span class="n">b</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> 
+<span class="p">})</span>
+<span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">bool</span> <span class="n">result</span><span class="p">){</span>
+  <span class="c1">// result is true if all inputs were zero</span>
+<span class="p">});</span>
+<span class="c1">// You could use onError or Try here in case one of your input Futures</span>
+<span class="c1">// contained an exception or if your reducing function threw an exception </span></pre></div>
+
+<p>To demonstrate the exception handling case, suppose you have a collection of <tt>Future&lt;T&gt;</tt> and you want a <tt>Future&lt;bool&gt;</tt> that contains true if all the input Futures are non-exceptional:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">reduce</span><span class="p">(</span><span class="n">fs</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="p">[](</span><span class="kt">bool</span> <span class="n">b</span><span class="p">,</span> <span class="n">Try</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">t</span><span class="p">){</span>
+  <span class="k">return</span> <span class="n">b</span> <span class="o">&amp;&amp;</span> <span class="n">t</span><span class="p">.</span><span class="n">hasValue</span><span class="p">();</span>
+<span class="p">})</span>
+<span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">bool</span> <span class="n">result</span><span class="p">){</span>
+  <span class="c1">// result is true if all inputs were non-exceptional</span>
+<span class="p">});</span></pre></div>
+
+<p>And finally one example where we&#039;re not reducing to a <tt>bool</tt> - here&#039;s how you might calculate the sum of a collection of <tt>Future&lt;int&gt;</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">reduce</span><span class="p">(</span><span class="n">fs</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">[](</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">){</span>
+  <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span>
+<span class="p">})</span>
+<span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="kt">int</span> <span class="n">sum</span><span class="p">){</span>
+  <span class="c1">// ...</span>
+<span class="p">});</span></pre></div>
+
+<p>See the <tt>reduce()</tt> tests in <a href="https://github.com/facebook/folly/blob/master/folly/futures/test/FutureTest.cpp" target="_blank">the Future tests</a> for a more complete catalog of possibilities.</p>
+
+<h2 id="unorderedreduce">unorderedReduce() <a href="#unorderedreduce" class="headerLink">#</a></h2>
+
+<p>Like <tt>reduce()</tt>, but consumes Futures in the collection as soon as they become ready. Use this if your function doesn&#039;t depend on the order of the Futures in the input collection. See the <a href="https://github.com/facebook/folly/blob/master/folly/futures/test/FutureTest.cpp#L1810" target="_blank">tests</a> for examples.</p>
+
+<h2 id="window">window() <a href="#window" class="headerLink">#</a></h2>
+
+<p><tt>window()</tt> is a sliding window implementation for Futures. It takes a collection of <tt>T</tt> (or a pair of iterators thereof), a function taking a <tt>T&amp;&amp;</tt> and returning a <tt>Future&lt;S&gt;</tt>, and a window size <tt>n</tt>. <tt>window()</tt> will create up to <tt>n</tt> Futures at a time using the function. As Futures complete, new Futures are created until the collection is exhausted.</p>
+
+<p>It ensures that at any given time, no more than <tt>n</tt> Futures are being processed.</p>
+
+<p>Combine with <tt>collectAll</tt>, <tt>reduce</tt> or <tt>unorderedReduce</tt>. See the <a href="https://github.com/facebook/folly/blob/master/folly/futures/test/FutureTest.cpp#L693" target="_blank">tests</a> for examples.</p>
+
+<h2 id="other-possibilities">Other Possibilities <a href="#other-possibilities" class="headerLink">#</a></h2>
+
+<p>There are a number of other possibilities for composing multiple Futures which we&#039;ll probably get around to at some point. If any of these seem like they would come in handy, let us know or better yet submit a diff:</p>
+
+<ul>
+<li><tt>filter()</tt></li>
+<li>&lt;your suggestion here&gt;</li>
+</ul></section><section class="dex_document"><h1>Multithreading and via()</h1><p class="dex_introduction">What to know and what to watch out for when using futures in a multithreaded environment, and how to control your threading model.</p><h2 id="futures-are-thread-safe">Futures are thread safe... with a catch. <a href="#futures-are-thread-safe" class="headerLink">#</a></h2>
+
+<p>The core mutating operations on Futures and Promises are thread safe, insofar as they will throw exceptions if misused (usually, this means being called more than once, including simultaneous calls from different threads). For example, <tt>then()</tt>, <tt>onError()</tt> and other methods that set callbacks on Futures will throw exceptions if called twice. The same goes for fulfilling Promises via <tt>setValue()</tt> and <tt>setException()</tt>.</p>
+
+<p>So what&#039;s the catch? Let&#039;s look at the following example of multithreaded Futures code:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// Thread A</span>
+<span class="n">Promise</span><span class="o">&lt;</span><span class="n">Unit</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+
+<span class="c1">// Thread B</span>
+<span class="n">f</span><span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
+
+<span class="c1">// Thread A</span>
+<span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">();</span></pre></div>
+
+<p>In which thread are x and y executed? Unfortunately, it depends. There is a race between setting the callbacks and fulfilling the promise. If setting the callbacks wins, they will be executed in thread A when the Promise is fulfilled. If setting the value wins, they will be executed in thread B as soon as they are set. If <tt>setValue()</tt> sneaks in at just the right time between the two <tt>then()</tt> calls, then x will be executed in thread A and y will be executed in thread B. You could imagine that this nondeterminism might become unwieldy or downright unacceptable. Thankfully, there&#039;s a mechanism to resolve this race and give you fine-grained control over your execution model.</p>
+
+<h2 id="via-to-the-rescue">via() to the rescue <a href="#via-to-the-rescue" class="headerLink">#</a></h2>
+
+<p>Futures have a method called <tt>via()</tt> which takes an <a href="https://github.com/facebook/folly/blob/master/folly/Executor.h#L27" target="_blank">Executor</a>. Executor is a simple interface that requires only the existence of an <tt>add(std::function&lt;void()&gt; func)</tt> method which must be thread safe and must execute the provided function somehow, though not necessarily immediately. <tt>via()</tt> guarantees that a callback set on the Future will be executed on the given Executor. For instance:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">makeFutureWith</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
+  <span class="p">.</span><span class="n">via</span><span class="p">(</span><span class="n">exe1</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
+  <span class="p">.</span><span class="n">via</span><span class="p">(</span><span class="n">exe2</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">z</span><span class="p">);</span></pre></div>
+
+<p>In this example, <tt>y</tt> will be executed on <tt>exe1</tt>, and <tt>z</tt> will be executed on <tt>exe2</tt>. This is a fairly powerful abstraction. It not only solves the above race, but gives you clear, concise, and self-documenting control over your execution model. One common pattern is having different executors for different types of work (e.g. an IO-bound pool spinning on event bases doing your network IO and a CPU-bound thread pool for expensive work) and switching between them with <tt>via()</tt>.</p>
+
+<p>There is also a static function <tt>via()</tt> that creates a completed <tt>Future&lt;Unit&gt;</tt> that is already set up to call back on the provided Executor, and <tt>via(Executor&amp;,Func)</tt> returns a Future for executing a function via an executor.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">via</span><span class="p">(</span><span class="n">exe</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
+<span class="n">via</span><span class="p">(</span><span class="n">exe</span><span class="p">,</span> <span class="n">a</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">b</span><span class="p">);</span></pre></div>
+
+<h2 id="or-pass-an-executor-to-t">Or, pass an Executor to <tt>then()</tt> <a href="#or-pass-an-executor-to-t" class="headerLink">#</a></h2>
+
+<p>An alternative to <tt>via()</tt> is to pass an Executor as the first parameter to <tt>then()</tt>, which causes the callback to be executed via that Executor. Unlike <tt>via()</tt> the Executor is not sticky, it only applies for this callback. See the docblock for more details and caveats.</p>
+
+<h2 id="executor-implementations">Executor implementations <a href="#executor-implementations" class="headerLink">#</a></h2>
+
+<p><tt>via()</tt> wouldn&#039;t be of much use without practical implementations around. We have a handful, and here&#039;s a (possibly incomplete) list.</p>
+
+<ul>
+<li><a href="https://github.com/facebook/wangle/blob/master/wangle/concurrent/ThreadPoolExecutor.h" target="_blank">ThreadPoolExecutor</a> is an abstract thread pool implementation that supports resizing, custom thread factories, pool and per-task stats, NUMA awareness, user-defined task expiration, and Codel task expiration. It and its subclasses are under active development. It currently has two implementations:<ul>
+<li><a href="https://github.com/facebook/wangle/blob/master/wangle/concurrent/CPUThreadPoolExecutor.h" target="_blank">CPUThreadPoolExecutor</a> is a general purpose thread pool. In addition to the above features, it also supports task priorities.</li>
+<li><a href="https://github.com/facebook/wangle/blob/master/wangle/concurrent/IOThreadPoolExecutor.h" target="_blank">IOThreadPoolExecutor</a> is similar to CPUThreadPoolExecutor, but each thread spins on an EventBase (accessible to callbacks via <a href="https://github.com/facebook/folly/blob/master/folly/io/async/EventBaseManager.h" target="_blank">EventBaseManager</a>)</li>
+</ul></li>
+<li>folly&#039;s <a href="https://github.com/facebook/folly/blob/master/folly/io/async/EventBase.h" target="_blank">EventBase</a> is an Executor and executes work as a callback in the event loop</li>
+<li><a href="https://github.com/facebook/folly/blob/master/folly/futures/ManualExecutor.h" target="_blank">ManualExecutor</a> only executes work when manually cranked. This is useful for testing.</li>
+<li><a href="https://github.com/facebook/folly/blob/master/folly/futures/InlineExecutor.h" target="_blank">InlineExecutor</a> executes work immediately inline</li>
+<li><a href="https://github.com/facebook/folly/blob/master/folly/futures/QueuedImmediateExecutor.h" target="_blank">QueuedImmediateExecutor</a> is similar to InlineExecutor, but work added during callback execution will be queued instead of immediately executed</li>
+<li><a href="https://github.com/facebook/folly/blob/master/folly/futures/ScheduledExecutor.h" target="_blank">ScheduledExecutor</a> is a subinterface of Executor that supports scheduled (i.e. delayed) execution. There aren&#039;t many implementations yet, see <a class="remarkup-task" href="https://our.intern.facebook.com/intern/tasks/?t=5924392" target="_blank">T5924392</a></li>
+<li>Thrift&#039;s <a href="https://github.com/facebook/fbthrift/blob/master/thrift/lib/cpp/concurrency/ThreadManager.h" target="_blank">ThreadManager</a> is an Executor but we aim to deprecate it in favor of the aforementioned CPUThreadPoolExecutor</li>
+<li><a href="https://github.com/facebook/wangle/blob/master/wangle/concurrent/FutureExecutor.h" target="_blank">FutureExecutor</a> wraps another Executor and provides <tt>Future&lt;T&gt; addFuture(F func)</tt> which returns a Future representing the result of func. This is equivalent to <tt>futures::async(executor, func)</tt> and the latter should probably be preferred.</li>
+</ul></section><section class="dex_document"><h1>Timeouts and related features</h1><p class="dex_introduction">Futures provide a number of timing-related features. Here's an overview.</p><h2 id="timing-implementation">Timing implementation <a href="#timing-implementation" class="headerLink">#</a></h2>
+
+<h3 id="timing-resolution">Timing resolution <a href="#timing-resolution" class="headerLink">#</a></h3>
+
+<p>The functions and methods documented below all take a <tt>Duration</tt>, <a href="https://github.com/facebook/folly/blob/master/folly/futures/detail/Types.h" target="_blank">which is an alias for <tt>std::chrono::milliseconds</tt></a>. Why not allow more granularity? Simply put, we can&#039;t guarantee sub-millisecond resolution and we don&#039;t want to lie to you.</p>
+
+<p>Do not use the <tt>Duration</tt> type directly, that defeats the point of using a <tt>std::chrono::duration</tt> type. Rather, use the appropriate <tt>std::chrono::duration</tt>, e.g. <tt>std::chrono::seconds</tt> or <tt>std::chrono::milliseconds</tt>.</p>
+
+<h3 id="the-timekeeper-interface">The TimeKeeper interface <a href="#the-timekeeper-interface" class="headerLink">#</a></h3>
+
+<p>Most timing-related methods also optionally take a <a href="https://github.com/facebook/folly/blob/master/folly/futures/Timekeeper.h#L44" target="_blank"><tt>TimeKeeper</tt></a>. Implement that interface if you&#039;d like control over how Futures timing works under the hood. If you don&#039;t provide a <tt>TimeKeeper</tt>, a default singleton will be lazily created and employed. The <a href="https://github.com/facebook/folly/blob/master/folly/futures/detail/ThreadWheelTimekeeper.h" target="_blank">default implementation</a> uses a folly::HHWheelTimer in a dedicated EventBase thread to manage timeouts.</p>
+
+<h2 id="within">within() <a href="#within" class="headerLink">#</a></h2>
+
+<p><tt>Future&lt;T&gt;::within()</tt> returns a new Future that will complete with the provided exception (by default, a TimedOut exception) if it does not complete within the specified duration. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">using</span> <span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">milliseconds</span><span class="p">;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">();</span>
+
+<span class="c1">// f will complete with a TimedOut exception if the Future returned by foo()</span>
+<span class="c1">// does not complete within 500 ms</span>
+<span class="n">f</span> <span class="o">=</span> <span class="n">foo</span><span class="p">().</span><span class="n">within</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">500</span><span class="p">));</span>
+
+<span class="c1">// Same deal, but a timeout will trigger the provided exception instead</span>
+<span class="n">f2</span> <span class="o">=</span> <span class="n">foo</span><span class="p">().</span><span class="n">within</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;you took too long!&quot;</span><span class="p">));</span></pre></div>
+
+<h2 id="ontimeout">onTimeout() <a href="#ontimeout" class="headerLink">#</a></h2>
+
+<p><tt>Future&lt;T&gt;::onTimeout()</tt> lets you simultaneously set up a timeout and a timeout handler. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">();</span>
+<span class="n">foo</span><span class="p">()</span>
+  <span class="p">.</span><span class="n">onTimeout</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span> <span class="p">[]{</span>
+    <span class="c1">// You must maintain the resultant future&#39;s type</span>
+    <span class="c1">// ... handle timeout ...</span>
+    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(...);</span></pre></div>
+
+<p>The astute reader might notice that this is effectively syntactic sugar for</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">foo</span><span class="p">()</span>
+  <span class="p">.</span><span class="n">within</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">500</span><span class="p">))</span>
+  <span class="p">.</span><span class="n">onError</span><span class="p">([](</span><span class="k">const</span> <span class="n">TimedOut</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
+    <span class="c1">// handle timeout</span>
+    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
+  <span class="p">})</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">(...);</span></pre></div>
+
+<h2 id="get-and-wait-with-timeou">get() and wait() with timeouts <a href="#get-and-wait-with-timeou" class="headerLink">#</a></h2>
+
+<p><tt>get()</tt> and <tt>wait()</tt>, which are detailed in the <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14606">Testing</a> article, optionally take timeouts:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">();</span>
+<span class="c1">// Will throw TimedOut if the Future doesn&#39;t complete within one second of</span>
+<span class="c1">// the get() call</span>
+<span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">foo</span><span class="p">().</span><span class="n">get</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>
+
+<span class="c1">// If the Future doesn&#39;t complete within one second, f will remain</span>
+<span class="c1">// incomplete. That is, if a timeout occurs, it&#39;s as if wait() was</span>
+<span class="c1">// never called.</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">foo</span><span class="p">().</span><span class="n">wait</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span></pre></div>
+
+<h2 id="delayed">delayed() <a href="#delayed" class="headerLink">#</a></h2>
+
+<p><tt>Future&lt;T&gt;::delayed()</tt> returns a new Future whose completion is delayed for at least the specified duration. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">makeFuture</span><span class="p">()</span>
+  <span class="p">.</span><span class="n">delayed</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">1000</span><span class="p">))</span>
+  <span class="p">.</span><span class="n">then</span><span class="p">([]{</span>
+    <span class="c1">// This will be executed when the original Future has completed or when</span>
+    <span class="c1">// 1000ms has elapsed, whichever comes last.</span>
+  <span class="p">});</span></pre></div>
+
+<h2 id="futures-sleep">futures::sleep() <a href="#futures-sleep" class="headerLink">#</a></h2>
+
+<p><tt>sleep()</tt> returns a <tt>Future&lt;Unit&gt;</tt> that will complete after the specified duration. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">futures</span><span class="o">::</span><span class="n">sleep</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">1000</span><span class="p">)).</span><span class="n">then</span><span class="p">([]{</span>
+  <span class="c1">// This will be executed after 1000ms</span>
+<span class="p">});</span></pre></div></section><section class="dex_document"><h1>Interrupts and Cancellations</h1><p class="dex_introduction">Interrupts are a mechanism for Future holders to send a signal to Promise holders. Here's how to use them.</p><p>Let&#039;s say that your Futures code kicks off some long, expensive operation in another thread. A short while later, something comes up that obviates the need for the result of that operation. Are those resources gone forever? Not necessarily. Enter interrupts.</p>
+
+<p>Interrupts allow Future holders to send signals in the form of exceptions to Promise holders, who are free to handle the interrupt as they please (or not at all). For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Promise</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">;</span>
+<span class="n">p</span><span class="p">.</span><span class="n">setInterruptHandler</span><span class="p">([](</span><span class="k">const</span> <span class="n">exception_wrapper</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">){</span>
+  <span class="c1">// Handle the interrupt. For instance, we could just fulfill the Promise</span>
+  <span class="c1">// with the given exception:</span>
+  <span class="n">p</span><span class="p">.</span><span class="n">setException</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
+  
+  <span class="c1">// Or maybe we want the Future to complete with some special value</span>
+  <span class="n">p</span><span class="p">.</span><span class="n">setValue</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
+
+  <span class="c1">// Or maybe we don&#39;t want to do anything at all! Including not setting</span>
+  <span class="c1">// this handler in the first place.</span>
+<span class="p">});</span>
+
+<span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">getFuture</span><span class="p">();</span>
+<span class="c1">// The Future holder can now send an interrupt whenever it wants via raise().</span>
+<span class="c1">// If the interrupt beats out the fulfillment of the Promise *and* there is</span>
+<span class="c1">// an interrupt handler set on the Promise, that handler will be called with</span>
+<span class="c1">// the provided exception</span>
+<span class="n">f</span><span class="p">.</span><span class="n">raise</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;Something went awry! Abort!&quot;</span><span class="p">));</span>
+
+<span class="c1">// cancel() is syntactic sugar for raise(FutureCancellation())</span>
+<span class="n">f</span><span class="p">.</span><span class="n">cancel</span><span class="p">();</span></pre></div>
+
+<p>Going forward, we&#039;d like to integrate interrupts with major Future interface provides as a way to cancel RPCs and the like, but that&#039;s not in place yet. This is a bleeding edge feature&#x2014;please let us know your use cases so that we can iterate!</p></section><section class="dex_document"><h1>Testing</h1><p class="dex_introduction">Testing futures-based code does not have to be a pain. Here are some tips and idiomatic approaches.</p><h2 id="extracting-values-synchr">Extracting values synchronously <a href="#extracting-values-synchr" class="headerLink">#</a></h2>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> The tests in this article are written using the <a href="https://code.google.com/p/googletest/wiki/Primer" target="_blank">gtest</a> framework.</div>
+
+<p>Let&#039;s say we want to test the following interface:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span> <span class="n">isPrime</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">);</span></pre></div>
+
+<p>We could make a couple of calls and set expectations on the resultant futures via <tt>value()</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">EXPECT_TRUE</span><span class="p">(</span><span class="n">isPrime</span><span class="p">(</span><span class="mi">7</span><span class="p">).</span><span class="n">value</span><span class="p">());</span>
+<span class="n">EXPECT_FALSE</span><span class="p">(</span><span class="n">isPrime</span><span class="p">(</span><span class="mi">8</span><span class="p">).</span><span class="n">value</span><span class="p">());</span></pre></div>
+
+<p>But what if <tt>isPrime()</tt> is asynchronous (e.g. makes an async call to another service that computes primeness)? It&#039;s now likely that you&#039;ll call <tt>value()</tt> before the Future is complete, which will throw a <a href="https://github.com/facebook/folly/blob/master/folly/futures/FutureException.h#L66" target="_blank"><tt>FutureNotReady</tt></a> exception.</p>
+
+<p>A naive approach is to spin until the Future is complete:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// Spin until ready. Gross. Don&#39;t do this.</span>
+<span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">isPrime</span><span class="p">(</span><span class="mi">7</span><span class="p">);</span>
+<span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">f</span><span class="p">.</span><span class="n">isReady</span><span class="p">())</span> <span class="p">{}</span>
+<span class="n">EXPECT_TRUE</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">value</span><span class="p">());</span></pre></div>
+
+<p>Thankfully, we have some better options in the form of <tt>Future&lt;T&gt;::get()</tt> and <tt>Future&lt;T&gt;::wait()</tt>.</p>
+
+<h3 id="get">get() <a href="#get" class="headerLink">#</a></h3>
+
+<p><tt>T Future&lt;T&gt;::get()</tt> blocks until the Future is complete and either returns a moved out copy of the value or throws any contained exception. You can use it like so.</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">EXPECT_TRUE</span><span class="p">(</span><span class="n">isPrime</span><span class="p">(</span><span class="mi">7</span><span class="p">).</span><span class="n">get</span><span class="p">());</span></pre></div>
+
+<p>Keep in mind that some other thread had better complete the Future, because the thread that calls <tt>get()</tt> will block. Also, <tt>get()</tt> optionally takes a timeout after which its throws a TimedOut exception. See the <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14677">Timeouts</a> article for more information.</p>
+
+<h3 id="wait">wait() <a href="#wait" class="headerLink">#</a></h3>
+
+<p><tt>Future&lt;T&gt; Future&lt;T&gt;::wait()</tt> is similar to <tt>get()</tt> in that it blocks until the Future is complete. However, instead of returning a value or throwing an exception, it returns a new completed Future with the result of the original Future. One use case is when you&#039;d rather not have the throwing behavior of <tt>get()</tt> so that you can check for exceptions separately without a try/catch. For example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">isPrime</span><span class="p">(</span><span class="mi">7</span><span class="p">).</span><span class="n">wait</span><span class="p">();</span>
+<span class="n">EXPECT_FALSE</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">getTry</span><span class="p">().</span><span class="n">hasException</span><span class="p">());</span>
+<span class="n">EXPECT_TRUE</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">value</span><span class="p">());</span></pre></div>
+
+<p>Like <tt>get()</tt>, <tt>wait()</tt> optionally takes a timeout. Again, see the <a href="https://our.intern.facebook.com/intern/dex/document/?doc_id=14677">Timeouts</a> article.</p>
+
+<h3 id="getvia-and-waitvia">getVia() and waitVia() <a href="#getvia-and-waitvia" class="headerLink">#</a></h3>
+
+<p><tt>T Future&lt;T&gt;::getVia(DrivableExecutor*)</tt> and <tt>Future&lt;T&gt; Future&lt;T&gt;::waitVia(DrivableExecutor*)</tt> have the same semantics as <tt>get()</tt> and <tt>wait()</tt> except that they drive some Executor until the Future is complete. <a href="https://github.com/facebook/folly/blob/master/folly/futures/DrivableExecutor.h" target="_blank"><tt>DrivableExecutor</tt></a> is a simple subinterface of <tt>Executor</tt> that requires the presence of a method <tt>drive()</tt> which can somehow make progress on the Executor&#039;s work. Two commonly helpful implementations are <a href="https://github.com/facebook/folly/blob/master/folly/io/async/EventBase.h" target="_blank"><tt>EventBase</tt></a> (where <tt>drive()</tt> loops on the EventBase) and <a href="https://github.com/facebook/folly/blob/master/folly/futures/ManualExecutor.h" target="_blank"><tt>ManualExecutor</tt></a>. These are simple but useful sugar for the following common pattern:</p>
+
+<p>Given this:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">doAsyncWorkOnEventBase</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eventBase</span><span class="p">);</span></pre></div>
+
+<p>Don&#039;t do this:</p>
+
+<div class="remarkup-code-block remarkup-counterexample" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">f</span><span class="p">.</span><span class="n">isReady</span><span class="p">())</span> <span class="p">{</span>
+  <span class="n">eb</span><span class="p">.</span><span class="n">loop</span><span class="p">();</span>
+<span class="p">}</span></pre></div>
+
+<p>Do one of these instead:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">val</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">getVia</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eventBase</span><span class="p">);</span>
+<span class="c1">// or</span>
+<span class="n">f</span><span class="p">.</span><span class="n">waitVia</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eb</span><span class="p">).</span><span class="n">then</span><span class="p">([](</span><span class="n">Value</span> <span class="n">val</span><span class="p">){</span> <span class="p">...</span> <span class="p">});</span></pre></div>
+
+<h2 id="using-gmock">Using gmock <a href="#using-gmock" class="headerLink">#</a></h2>
+
+<p><a href="https://code.google.com/p/googlemock/" target="_blank">Google Mock</a> is a powerful mocking framework for writing and using C++ mock classes. Unfortunately, Gmock requires that the parameters and return types of mocked functions and methods are copyable. You&#039;re likely to hit this issue when mocking Futures code because Futures (and, less importantly, Promises) are noncopyable, and many of your interfaces will return Futures and some will even be passed Futures.</p>
+
+<p>The canonical approach to mocking interfaces that involve noncopyable objects is to override your interface with a dummy method that simply calls a mock method that has had the noncopyable components stripped or replaced. For Futures, this usually means returning/passing contained values directly and synchronously, which shouldn&#039;t be a problem since your mocks won&#039;t actually be asynchronous. Here is a very contrived but demonstrative example:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// The async interface we want to mock</span>
+<span class="k">class</span> <span class="nc">AsyncClient</span> <span class="p">{</span>
+ <span class="nl">public:</span>
+  <span class="k">virtual</span> <span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">);</span>
+<span class="p">};</span>
+
+<span class="c1">// The mock implementation</span>
+<span class="k">class</span> <span class="nc">MockAsyncClient</span> <span class="o">:</span> <span class="k">public</span> <span class="n">AsyncClient</span> <span class="p">{</span>
+ <span class="nl">public:</span>
+  <span class="c1">// Declare a mock method foo_ that takes an int and returns an int</span>
+  <span class="n">MOCK_METHOD1</span><span class="p">(</span><span class="n">foo_</span><span class="p">,</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
+
+  <span class="c1">// Plug the mock into an override of the interface</span>
+  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="n">override</span> <span class="p">{</span>
+    <span class="c1">// Lift the result back into a Future before returning</span>
+    <span class="k">return</span> <span class="n">makeFuture</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">foo_</span><span class="p">(</span><span class="n">i</span><span class="p">));</span>
+  <span class="p">}</span>
+<span class="p">};</span>
+
+<span class="c1">// Let&#39;s say that we&#39;re testing a class MyProxy that simply forwards foo()</span>
+<span class="c1">// calls to AsyncClient and returns the result</span>
+<span class="k">class</span> <span class="nc">MyProxy</span> <span class="p">{</span>
+ <span class="nl">public:</span>
+  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
+    <span class="k">return</span> <span class="n">client</span><span class="o">-&gt;</span><span class="n">foo</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
+  <span class="p">}</span>
+  <span class="kt">void</span> <span class="n">setClient</span><span class="p">(</span><span class="n">AsyncClient</span><span class="o">*</span> <span class="n">client</span><span class="p">);</span>
+ <span class="nl">private:</span>
+  <span class="n">AsyncClient</span><span class="o">*</span> <span class="n">client</span><span class="p">;</span>
+<span class="p">};</span>
+
+<span class="c1">// Now, in our testing code</span>
+<span class="n">MyProxy</span> <span class="n">proxy</span><span class="p">;</span>
+<span class="n">MockAsyncClient</span> <span class="n">mockClient</span><span class="p">;</span>
+<span class="c1">// Inject the mock</span>
+<span class="n">proxy</span><span class="p">.</span><span class="n">setClient</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mockClient</span><span class="p">)</span>
+<span class="c1">// Set an expectation on the mock to be called with 42 and return 84</span>
+<span class="n">EXPECT_CALL</span><span class="p">(</span><span class="n">mockClient</span><span class="p">,</span> <span class="n">foo_</span><span class="p">(</span><span class="mi">42</span><span class="p">)).</span><span class="n">WillOnce</span><span class="p">(</span><span class="n">Return</span><span class="p">(</span><span class="mi">84</span><span class="p">));</span>
+<span class="c1">// Trigger the call</span>
+<span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">MyProxy</span><span class="p">.</span><span class="n">foo</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
+<span class="c1">// If everything has been mocked out synchronously, we can just check the</span>
+<span class="c1">// value of the future directly</span>
+<span class="n">EXPECT_EQ</span><span class="p">(</span><span class="mi">84</span><span class="p">,</span> <span class="n">f</span><span class="p">.</span><span class="n">value</span><span class="p">());</span></pre></div></section><section class="dex_document"><h1>Pitfalls</h1><p class="dex_introduction"></p><h2 id="eventbase-eventbasemanag">EventBase, EventBaseManager, Executor <a href="#eventbase-eventbasemanag" class="headerLink">#</a></h2>
+
+<p>It&#039;s not uncommon to hit a snag (especially when using via()) where you&#039;re hanging for (a) being on the wrong thread (b) talking to an EventBase which is not actually spinning (loopForever).</p>
+
+<p>Some tips:</p>
+
+<ul>
+<li>evb-&gt;isInRunningEventBase()</li>
+<li>evb-&gt;isRunning()</li>
+</ul>
+
+<h2 id="lambda-arguments">Lambda Arguments <a href="#lambda-arguments" class="headerLink">#</a></h2>
+
+<p>The danger with lambdas is you&#039;ll try to read a variable that&#039;s gone</p>
+
+<div class="remarkup-code-block remarkup-counterexample" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Object</span> <span class="n">obj</span> <span class="o">=</span> <span class="p">...;</span>
+<span class="k">return</span> <span class="n">future1</span><span class="p">.</span><span class="n">then</span><span class="p">([</span><span class="o">&amp;</span><span class="p">]</span> <span class="p">{</span>
+    <span class="c1">// ..work..</span>
+    <span class="n">obj</span><span class="p">.</span><span class="n">method</span><span class="p">();</span>
+      <span class="c1">// woops object is gone from the </span>
+      <span class="c1">// stack when this actually runs</span>
+<span class="p">});</span></pre></div>
+
+<p>Sometimes it makes sense to copy inputs. Sometimes that&#039;s too expensive and a shared_ptr is best. Sometimes the nature of things lends itself to the contract &quot;this won&#039;t go away&quot; and you take a raw pointer, but this should only be used when it&#039;s a very natural fit. In particular, you don&#039;t want people wishing you took a shared pointer and having to do something like this to work around it:</p>
+
+<div class="remarkup-code-block remarkup-counterexample" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">make_shared</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span><span class="p">();</span>
+<span class="n">yourFunction</span><span class="p">(</span><span class="n">foo</span><span class="p">.</span><span class="n">get</span><span class="p">(),</span>
+  <span class="p">[</span><span class="n">foo</span><span class="p">]{</span> 
+     <span class="cm">/* callback doesn&#39;t use foo, but captures the </span>
+<span class="cm">      * shared pointer to keep it alive </span>
+<span class="cm">      */</span>
+<span class="p">});</span></pre></div>
+
+<p>In general:
+prefer taking inputs by value if they&#039;re small enough
+if inputs are big (measurably expensive to copy), then keep them on the heap and prefer a shared_ptr
+if you are really sure you need to get more fancy, put on your wizard hat and go to it ;)</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="n">Object</span> <span class="n">obj</span> <span class="o">=</span> <span class="p">...;</span>
+<span class="k">return</span> <span class="n">future1</span><span class="p">.</span><span class="n">then</span><span class="p">([</span><span class="n">obj</span><span class="p">]</span> <span class="p">{</span>  <span class="c1">// capture by value</span>
+    <span class="c1">// ..work..</span>
+    <span class="n">obj</span><span class="p">.</span><span class="n">method</span><span class="p">();</span>
+      <span class="c1">// works on copy of obj</span>
+<span class="p">});</span></pre></div>
+
+<p>If Object is large:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">auto</span> <span class="n">optr</span> <span class="o">=</span> <span class="n">makeShared</span><span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span><span class="p">(...);</span>
+<span class="k">return</span> <span class="n">future1</span><span class="p">.</span><span class="n">then</span><span class="p">([</span><span class="n">optr</span><span class="p">]</span> <span class="p">{</span>  <span class="c1">// copy ptr, use count = 2</span>
+    <span class="c1">// ..work..</span>
+    <span class="n">optr</span><span class="o">-&gt;</span><span class="n">method</span><span class="p">();</span>
+      <span class="c1">// works on original object</span>
+    <span class="c1">// use-count for optr goes to 0 and object destructs</span>
+<span class="p">});</span></pre></div></section><section class="dex_document"><h1>Future as a Monad</h1><p class="dex_introduction">A semi-formal and totally optional analysis of Future as a monad.</p><p>Future is a monad. You don&#039;t need to know this or what it means to use Futures, but if you are curious, want to understand monads better, or eat functional flakes for breakfast, then keep reading this extremely extracurricular document.</p>
+
+<p>Let&#039;s review the definition of a monad. Formal definitions are mathematical and/or in Haskellese and therefore opaque to imperative mortals. But here&#039;s a simplified description using a subset of Haskell type notation that is useful but not confusing:</p>
+
+<div class="remarkup-code-block" data-code-lang="hs"><pre class="remarkup-code"><span class="c1">-- &quot;unit&quot; is a function that takes a value and wraps it in the monad type.</span>
+<span class="c1">-- Haskellers call this &quot;return&quot; as some kind of sick inside joke.</span>
+<span class="nf">unit</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">a</span>
+
+<span class="c1">-- &quot;bind&quot; is a function that takes a monad, and a function that takes a value</span>
+<span class="c1">-- and returns another monad. Haskellers call this &quot;&gt;&gt;=&quot; because they are</span>
+<span class="c1">-- vying to unseat perl from the throne of illegibility.</span>
+<span class="nf">bind</span> <span class="ow">::</span> <span class="n">m</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">b</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">b</span></pre></div>
+
+<p>Monads must also satisfy these three axioms:</p>
+
+<div class="remarkup-code-block" data-code-lang="hs"><pre class="remarkup-code"><span class="c1">-- Left Identity</span>
+<span class="nf">unit</span> <span class="n">a</span> <span class="p">`</span><span class="n">bind</span><span class="p">`</span> <span class="n">f</span> <span class="err">≡</span> <span class="n">f</span> <span class="n">a</span>
+<span class="c1">-- Right Identity</span>
+<span class="nf">m</span> <span class="p">`</span><span class="n">bind</span><span class="p">`</span> <span class="n">unit</span> <span class="err">≡</span> <span class="n">m</span>
+<span class="c1">-- Associativity</span>
+<span class="p">(</span><span class="n">m</span> <span class="p">`</span><span class="n">bind</span><span class="p">`</span> <span class="n">f</span><span class="p">)</span> <span class="p">`</span><span class="n">bind</span><span class="p">`</span> <span class="n">g</span> <span class="err">≡</span> <span class="n">m</span> <span class="p">`</span><span class="n">bind</span><span class="p">`</span> <span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="ow">-&gt;</span> <span class="n">f</span> <span class="n">x</span> <span class="p">`</span><span class="n">bind</span><span class="p">`</span> <span class="n">g</span><span class="p">)</span></pre></div>
+
+<p>I won&#039;t try to explain that, there are <a href="http://lmgtfy.com/?q=what+the+hell+is+a+monad%3F" target="_blank">many blog posts and wiki pages that try to do that</a>. Instead, I&#039;ll substitute the equivalent Future monad expressions, and the whole thing will (probably) start to make sense. First, a simplified Future type:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">A</span><span class="o">&gt;</span>
+<span class="k">struct</span> <span class="n">Future</span> <span class="p">{</span>
+  <span class="c1">// The constructor that takes a value is &quot;unit&quot;</span>
+  <span class="n">Future</span><span class="p">(</span><span class="n">A</span><span class="p">);</span>
+
+  <span class="c1">// &quot;then&quot; is &quot;bind&quot;</span>
+  <span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">B</span><span class="o">&gt;</span>
+  <span class="n">Future</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span> <span class="n">then</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span><span class="p">(</span><span class="n">A</span><span class="p">));</span>
+
+  <span class="p">...</span>
+<span class="p">};</span>
+
+<span class="c1">// &quot;makeFuture&quot; is also &quot;unit&quot;, and we will need it because constructors can&#39;t</span>
+<span class="c1">// really be converted to std::function (AFAIK)</span>
+<span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">A</span><span class="o">&gt;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span> <span class="n">makeFuture</span><span class="p">(</span><span class="n">A</span><span class="p">);</span></pre></div>
+
+<p>Now, the three axioms (Futures don&#039;t define <tt>operator==</tt> but you get the idea):</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// Left Identity</span>
+<span class="n">A</span> <span class="n">a</span><span class="p">;</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span><span class="p">(</span><span class="n">a</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="o">==</span> <span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
+
+<span class="c1">// Right Identity</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>
+<span class="n">m</span><span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">makeFuture</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">==</span> <span class="n">m</span>
+
+<span class="c1">// Associativity</span>
+<span class="n">Future</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>
+<span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">f</span><span class="p">;</span>
+<span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">C</span><span class="o">&gt;</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">g</span><span class="p">;</span>
+<span class="n">m</span><span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">f</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">g</span><span class="p">)</span> <span class="o">==</span> <span class="n">m</span><span class="p">.</span><span class="n">then</span><span class="p">([](</span><span class="n">A</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">then</span><span class="p">(</span><span class="n">g</span><span class="p">);</span> <span class="p">})</span></pre></div>
+
+<p>So, in plain english this says a monad like Future has a way to get stuff in the monad (unit/makeFuture), and a way to chain things together (bind/then). unit semantics are unsurprising, and chaining is the same as nesting. Something that behaves this way is a monad, and Future is a monad.</p>
+
+<div class="remarkup-note">Remember how Futures do more than just hold values? The nature of the underlying asynchronous operations (usually I/O) generally includes side effects, and this breaks our pure formalism. You may or may not be able to make your async operations (observable) side-effect free, but you can make your intermediate Future callbacks functionally pure (aka value semantics), and if you do you will be happier than if you mutate state. But I won&#039;t beat that dead horse here&#x2014;I know you will probably mutate state anyway because you&#039;re a perf-conscious C++ developer and speed trumps safety. But do try to minimize it.</div>
+
+<p>Ok, so now we know Future is a monad. What can we do with this newfound power? Knowledge is power, right? Well, you can brag to your friends, for one thing. C++ doesn&#039;t really provide any concrete reusable tools for things that are monads. There&#039;s no do-blocks, or some generic monad-aware functional toolkit that includes map, filter, fold, etc. But what you do get is a way of thinking about and reasoning about your Futures that transcends our own little implementation, and doesn&#039;t require that you grok all the opaque internals of the implementation to do it.</p>
+
+<p>But mostly it makes you cool.</p>
+
+<h3 id="kleisli-composition-extr">Kleisli Composition (extra extra credit) <a href="#kleisli-composition-extr" class="headerLink">#</a></h3>
+
+<p>If &quot;associative&quot; doesn&#039;t look associative to you, then you are very astute. Congratulations! You win a maths unicorn.
+The three laws refer to a different formulation of the axioms, in terms of the Kleisli Composition operator (<tt>&gt;=&gt;</tt>), which basically says compose two monad-making functions in the obvious way.</p>
+
+<div class="remarkup-code-block" data-code-lang="hs"><pre class="remarkup-code"><span class="p">(</span><span class="o">&gt;=&gt;</span><span class="p">)</span> <span class="ow">::</span> <span class="kt">Monad</span> <span class="n">m</span> <span class="ow">=&gt;</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">b</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="n">b</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">c</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">c</span>
+
+<span class="c1">-- Left Identity</span>
+<span class="nf">unit</span> <span class="o">&gt;=&gt;</span> <span class="n">g</span> <span class="err">≡</span> <span class="n">g</span>
+<span class="c1">-- Right Identity</span>
+<span class="nf">f</span> <span class="o">&gt;=&gt;</span> <span class="n">unit</span> <span class="err">≡</span> <span class="n">f</span>
+<span class="c1">-- Associativity</span>
+<span class="p">(</span><span class="n">f</span> <span class="o">&gt;=&gt;</span> <span class="n">g</span><span class="p">)</span> <span class="o">&gt;=&gt;</span> <span class="n">h</span> <span class="err">≡</span> <span class="n">f</span> <span class="o">&gt;=&gt;</span> <span class="p">(</span><span class="n">g</span> <span class="o">&gt;=&gt;</span> <span class="n">h</span><span class="p">)</span></pre></div>
+
+<p>We accidentally implemented this operator, and called it <tt>chain</tt>. Then we removed it in favor of <tt>Future::thenMulti</tt>. But it totally existed, so use your imagination:</p>
+
+<div class="remarkup-code-block" data-code-lang="cpp"><pre class="remarkup-code"><span class="c1">// Left Identity</span>
+<span class="n">chain</span><span class="p">(</span><span class="n">makeFuture</span><span class="p">,</span> <span class="n">g</span><span class="p">)</span> <span class="err">≡</span> <span class="n">g</span>
+<span class="c1">// Right Identity</span>
+<span class="n">chain</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">makeFuture</span><span class="p">)</span> <span class="err">≡</span> <span class="n">f</span>
+<span class="c1">// Associativity</span>
+<span class="n">chain</span><span class="p">(</span><span class="n">chain</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">g</span><span class="p">),</span> <span class="n">h</span><span class="p">)</span> <span class="err">≡</span> <span class="n">chain</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">chain</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">h</span><span class="p">))</span> <span class="c1">// and chain(f, g, h)</span></pre></div>
+
+<h3 id="further-reading">Further reading <a href="#further-reading" class="headerLink">#</a></h3>
+
+<ul>
+<li><a href="https://wiki.haskell.org/Monad_laws" target="_blank">https://wiki.haskell.org/Monad_laws</a></li>
+<li><a href="http://learnyouahaskell.com/a-fistful-of-monads" target="_blank">http://learnyouahaskell.com/a-fistful-of-monads</a></li>
+</ul></section><section class="dex_document"><h1>FAQ</h1><p class="dex_introduction"></p><h2 id="what-s-this-unit-thing-i">What&#039;s this <tt>Unit</tt> thing? I&#039;m confused. <a href="#what-s-this-unit-thing-i" class="headerLink">#</a></h2>
+
+<p>If your callback returns <tt>void</tt>, it will result in a <tt>Future&lt;Unit&gt;</tt>. <tt>Future&lt;void&gt;</tt> is illegal. All you need to know is, if you would expect a <tt>Future&lt;void&gt;</tt> or <tt>Promise&lt;void&gt;</tt> or <tt>Try&lt;void&gt;</tt>, type <tt>Unit</tt> instead of <tt>void</tt>.</p>
+
+<h2 id="why-not-use-std-future">Why not use <tt>std::future</tt>? <a href="#why-not-use-std-future" class="headerLink">#</a></h2>
+
+<p>No callback support. See also <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3428.pdf" target="_blank">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3428.pdf</a></p>
+
+<h2 id="why-not-use-boost-future">Why not use boost::future? <a href="#why-not-use-boost-future" class="headerLink">#</a></h2>
+
+<ul>
+<li>At the time of writing, 1.53 (the first version with the requisite features) was brand new, not well-tested, and not available to Facebook developers.</li>
+<li>It is still a bit buggy/bleeding-edge</li>
+<li>They haven&#039;t fleshed out the threading model very well yet, e.g. every single then currently spawns a new thread unless you explicitly ask it to work on this thread only, and executor support was nonexistent (and now, is still experimental).</li>
+</ul>
+
+<h2 id="why-use-heap-allocated-s">Why use heap-allocated shared state? Why is Promise not a subclass of Future (like Twitter&#039;s)? <a href="#why-use-heap-allocated-s" class="headerLink">#</a></h2>
+
+<p>C++. It boils down to wanting to return a Future by value for performance (move semantics and compiler optimizations), and programmer sanity, and needing a reference to the shared state by both the user (which holds the Future) and the asynchronous operation (which holds the Promise), and allowing either to go out of scope.</p>
+
+<h2 id="what-about-proper-contin">What about proper continuations (fibers)? Futures suck. <a href="#what-about-proper-contin" class="headerLink">#</a></h2>
+
+<p>People mean two things here, they either mean using continuations (as in CSP) or they mean using generators which require continuations. It&#039;s important to know those are two distinct questions, but in our context the answer is the same because continuations are a prerequisite for generators.</p>
+
+<p>C++ doesn&#039;t directly support continuations very well. But there are some ways to do them in C/C++ that rely on some rather low-level facilities like <tt>setjmp</tt> and <tt>longjmp</tt> (among others). So yes, they are possible (cf. <a href="https://github.com/ccutrer/mordor" target="_blank">Mordor</a> and <a href="https://github.com/facebook/folly/tree/master/folly/experimental/fibers" target="_blank">folly/experimental/fibers</a>).</p>
+
+<p>The tradeoff is memory. Each continuation has a stack, and that stack is usually fixed-size and has to be big enough to support whatever ordinary computation you might want to do on it. So each living continuation requires a relatively large amount of memory. If you know the number of continuations will be small, this might be a good fit. In particular, it might be faster, the code might read cleaner, and debugging stack traces might be much easier.</p>
+
+<p>Futures takes the middle road between callback hell and continuations, one which has been trodden and proved useful in other languages. It doesn&#039;t claim to be the best model for all situations. Use your tools wisely.</p></section></section>
\ No newline at end of file
diff --git a/faux-folly/folly/futures/ScheduledExecutor.cpp b/faux-folly/folly/futures/ScheduledExecutor.cpp
new file mode 100644
index 0000000..7f18c4e
--- /dev/null
+++ b/faux-folly/folly/futures/ScheduledExecutor.cpp
@@ -0,0 +1,10 @@
+#include <folly/futures/ScheduledExecutor.h>
+
+namespace folly {
+
+void ScheduledExecutor::schedule(Func&& a, ScheduledExecutor::Duration const& dur)
+{
+    scheduleAt(std::move(a), now() + dur);
+}
+
+} // folly
diff --git a/faux-folly/folly/futures/ScheduledExecutor.h b/faux-folly/folly/futures/ScheduledExecutor.h
new file mode 100644
index 0000000..0bdfc73
--- /dev/null
+++ b/faux-folly/folly/futures/ScheduledExecutor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 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/Executor.h>
+#include <chrono>
+#include <memory>
+#include <stdexcept>
+
+namespace folly {
+  // An executor that supports timed scheduling. Like RxScheduler.
+  class ScheduledExecutor : public virtual Executor {
+   public:
+     // Reality is that better than millisecond resolution is very hard to
+     // achieve. However, we reserve the right to be incredible.
+     typedef std::chrono::microseconds Duration;
+     typedef std::chrono::steady_clock::time_point TimePoint;
+
+     virtual ~ScheduledExecutor() = default;
+
+     virtual void add(Func) override = 0;
+
+     /// Alias for add() (for Rx consistency)
+     void schedule(Func&& a) { add(std::move(a)); }
+
+     /// Schedule a Func to be executed after dur time has elapsed
+     /// Expect millisecond resolution at best.
+     void schedule(Func&& a, Duration const& dur);
+
+     /// Schedule a Func to be executed at time t, or as soon afterward as
+     /// possible. Expect millisecond resolution at best. Must be threadsafe.
+     virtual void scheduleAt(Func&& a, TimePoint const& t) {
+       throw std::logic_error("unimplemented");
+     }
+
+     /// Get this executor's notion of time. Must be threadsafe.
+     virtual TimePoint now() {
+       return std::chrono::steady_clock::now();
+     }
+  };
+}
diff --git a/faux-folly/folly/futures/SharedPromise-inl.h b/faux-folly/folly/futures/SharedPromise-inl.h
new file mode 100644
index 0000000..2c09a2a
--- /dev/null
+++ b/faux-folly/folly/futures/SharedPromise-inl.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015 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
+
+namespace folly {
+
+template <class T>
+SharedPromise<T>::SharedPromise(SharedPromise<T>&& other) noexcept {
+  *this = std::move(other);
+}
+
+template <class T>
+SharedPromise<T>& SharedPromise<T>::operator=(
+    SharedPromise<T>&& other) noexcept {
+  if (this == &other) {
+    return *this;
+  }
+
+  // std::lock will perform deadlock avoidance, in case
+  // Thread A: p1 = std::move(p2)
+  // Thread B: p2 = std::move(p1)
+  // race each other
+  std::lock(mutex_, other.mutex_);
+  std::lock_guard<std::mutex> g1(mutex_, std::adopt_lock);
+  std::lock_guard<std::mutex> g2(other.mutex_, std::adopt_lock);
+
+  std::swap(size_, other.size_);
+  std::swap(hasValue_, other.hasValue_);
+  std::swap(try_, other.try_);
+  std::swap(promises_, other.promises_);
+
+  return *this;
+}
+
+template <class T>
+size_t SharedPromise<T>::size() {
+  std::lock_guard<std::mutex> g(mutex_);
+  return size_;
+}
+
+template <class T>
+Future<T> SharedPromise<T>::getFuture() {
+  std::lock_guard<std::mutex> g(mutex_);
+  size_++;
+  if (hasValue_) {
+    return makeFuture<T>(Try<T>(try_));
+  } else {
+    promises_.emplace_back();
+    return promises_.back().getFuture();
+  }
+}
+
+template <class T>
+template <class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
+SharedPromise<T>::setException(E const& e) {
+  setTry(Try<T>(e));
+}
+
+template <class T>
+void SharedPromise<T>::setException(std::exception_ptr const& ep) {
+  setTry(Try<T>(ep));
+}
+
+template <class T>
+void SharedPromise<T>::setException(exception_wrapper ew) {
+  setTry(Try<T>(std::move(ew)));
+}
+
+template <class T>
+void SharedPromise<T>::setInterruptHandler(
+    std::function<void(exception_wrapper const&)> fn) {
+  std::lock_guard<std::mutex> g(mutex_);
+  if (hasValue_) {
+    return;
+  }
+  for (auto& p : promises_) {
+    p.setInterruptHandler(fn);
+  }
+}
+
+template <class T>
+template <class M>
+void SharedPromise<T>::setValue(M&& v) {
+  setTry(Try<T>(std::forward<M>(v)));
+}
+
+template <class T>
+template <class F>
+void SharedPromise<T>::setWith(F&& func) {
+  setTry(makeTryFunction(std::forward<F>(func)));
+}
+
+template <class T>
+void SharedPromise<T>::setTry(Try<T>&& t) {
+  std::vector<Promise<T>> promises;
+
+  {
+    std::lock_guard<std::mutex> g(mutex_);
+    if (hasValue_) {
+      throw PromiseAlreadySatisfied();
+    }
+    hasValue_ = true;
+    try_ = std::move(t);
+    promises.swap(promises_);
+  }
+
+  for (auto& p : promises) {
+    p.setTry(Try<T>(try_));
+  }
+}
+
+}
diff --git a/faux-folly/folly/futures/SharedPromise.h b/faux-folly/folly/futures/SharedPromise.h
new file mode 100644
index 0000000..dc37e00
--- /dev/null
+++ b/faux-folly/folly/futures/SharedPromise.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2015 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/futures/Promise.h>
+#include <folly/Portability.h>
+
+namespace folly {
+
+/*
+ * SharedPromise provides the same interface as Promise, but you can extract
+ * multiple Futures from it, i.e. you can call getFuture() as many times as
+ * you'd like. When the SharedPromise is fulfilled, all of the Futures will be
+ * called back. Calls to getFuture() after the SharedPromise is fulfilled return
+ * a completed Future. If you find yourself constructing collections of Promises
+ * and fulfilling them simultaneously with the same value, consider this
+ * utility instead. Likewise, if you find yourself in need of setting multiple
+ * callbacks on the same Future (which is indefinitely unsupported), consider
+ * refactoring to use SharedPromise to "split" the Future.
+ */
+template <class T>
+class SharedPromise {
+public:
+  SharedPromise() = default;
+  ~SharedPromise() = default;
+
+  // not copyable
+  SharedPromise(SharedPromise const&) = delete;
+  SharedPromise& operator=(SharedPromise const&) = delete;
+
+  // movable
+  SharedPromise(SharedPromise<T>&&) noexcept;
+  SharedPromise& operator=(SharedPromise<T>&&) noexcept;
+
+  /**
+   * Return a Future tied to the shared core state. Unlike Promise::getFuture,
+   * this can be called an unlimited number of times per SharedPromise.
+   */
+  Future<T> getFuture();
+
+  /** Return the number of Futures associated with this SharedPromise */
+  size_t size();
+
+  /** Fulfill the SharedPromise with an exception_wrapper */
+  void setException(exception_wrapper ew);
+
+  /** Fulfill the SharedPromise with an exception_ptr, e.g.
+    try {
+      ...
+    } catch (...) {
+      p.setException(std::current_exception());
+    }
+    */
+  FOLLY_DEPRECATED("use setException(exception_wrapper)")
+  void setException(std::exception_ptr const&);
+
+  /** Fulfill the SharedPromise with an exception type E, which can be passed to
+    std::make_exception_ptr(). Useful for originating exceptions. If you
+    caught an exception the exception_wrapper form is more appropriate.
+    */
+  template <class E>
+  typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
+  setException(E const&);
+
+  /// Set an interrupt handler to handle interrupts. See the documentation for
+  /// Future::raise(). Your handler can do whatever it wants, but if you
+  /// bother to set one then you probably will want to fulfill the SharedPromise with
+  /// an exception (or special value) indicating how the interrupt was
+  /// handled.
+  void setInterruptHandler(std::function<void(exception_wrapper const&)>);
+
+  /// Sugar to fulfill this SharedPromise<Unit>
+  template <class B = T>
+  typename std::enable_if<std::is_same<Unit, B>::value, void>::type
+  setValue() {
+    setTry(Try<T>(T()));
+  }
+
+  /** Set the value (use perfect forwarding for both move and copy) */
+  template <class M>
+  void setValue(M&& value);
+
+  void setTry(Try<T>&& t);
+
+  /** Fulfill this SharedPromise with the result of a function that takes no
+    arguments and returns something implicitly convertible to T.
+    Captures exceptions. e.g.
+
+    p.setWith([] { do something that may throw; return a T; });
+  */
+  template <class F>
+  void setWith(F&& func);
+
+private:
+  std::mutex mutex_;
+  size_t size_{0};
+  bool hasValue_{false};
+  Try<T> try_;
+  std::vector<Promise<T>> promises_;
+};
+
+}
+
+#include <folly/futures/Future.h>
+#include <folly/futures/SharedPromise-inl.h>
diff --git a/faux-folly/folly/futures/ThreadedExecutor.cpp b/faux-folly/folly/futures/ThreadedExecutor.cpp
new file mode 100644
index 0000000..bfa0db6
--- /dev/null
+++ b/faux-folly/folly/futures/ThreadedExecutor.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 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/futures/ThreadedExecutor.h>
+
+#include <glog/logging.h>
+
+using namespace std;
+
+namespace folly { namespace futures {
+
+ThreadedExecutor::~ThreadedExecutor() {
+  lock_guard<mutex> lock(mutex_);
+  destructing_ = true;
+  for (auto& th : threads_) {
+    th.join();
+  }
+}
+
+void ThreadedExecutor::add(Func f) {
+  lock_guard<mutex> lock(mutex_);
+  CHECK(!destructing_);
+  threads_.emplace_back(std::move(f));
+}
+
+}}
diff --git a/faux-folly/folly/futures/ThreadedExecutor.h b/faux-folly/folly/futures/ThreadedExecutor.h
new file mode 100644
index 0000000..f538c41
--- /dev/null
+++ b/faux-folly/folly/futures/ThreadedExecutor.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 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 <list>
+#include <mutex>
+#include <thread>
+#include <folly/Executor.h>
+
+namespace folly { namespace futures {
+
+/**
+ *  Runs functions each in its own thread.
+ *
+ *  Kind of simple. Suitable for a few types of strange cases.
+ */
+class ThreadedExecutor : public Executor {
+public:
+  ~ThreadedExecutor();
+  void add(Func f) override;
+private:
+  std::mutex mutex_;
+  std::list<std::thread> threads_;
+  bool destructing_ = false;
+};
+
+}}
diff --git a/faux-folly/folly/futures/Timekeeper.h b/faux-folly/folly/futures/Timekeeper.h
new file mode 100644
index 0000000..8a78b8f
--- /dev/null
+++ b/faux-folly/folly/futures/Timekeeper.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2015 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/futures/detail/Types.h>
+#include <folly/futures/Unit.h>
+
+namespace folly {
+
+template <class> class Future;
+
+/// A Timekeeper handles the details of keeping time and fulfilling delay
+/// promises. The returned Future<Unit> will either complete after the
+/// elapsed time, or in the event of some kind of exceptional error may hold
+/// an exception. These Futures respond to cancellation. If you use a lot of
+/// Delays and many of them ultimately are unneeded (as would be the case for
+/// Delays that are used to trigger timeouts of async operations), then you
+/// can and should cancel them to reclaim resources.
+///
+/// Users will typically get one of these via Future::sleep(Duration) or
+/// use them implicitly behind the scenes by passing a timeout to some Future
+/// operation.
+///
+/// Although we don't formally alias Delay = Future<Unit>,
+/// that's an appropriate term for it. People will probably also call these
+/// Timeouts, and that's ok I guess, but that term is so overloaded I thought
+/// it made sense to introduce a cleaner term.
+///
+/// Remember that Duration is a std::chrono duration (millisecond resolution
+/// at the time of writing). When writing code that uses specific durations,
+/// prefer using the explicit std::chrono type, e.g. std::chrono::milliseconds
+/// over Duration. This makes the code more legible and means you won't be
+/// unpleasantly surprised if we redefine Duration to microseconds, or
+/// something.
+///
+///    timekeeper.after(std::chrono::duration_cast<Duration>(
+///      someNanoseconds))
+class Timekeeper {
+ public:
+  virtual ~Timekeeper() = default;
+
+  /// Returns a future that will complete after the given duration with the
+  /// elapsed time. Exceptional errors can happen but they must be
+  /// exceptional. Use the steady (monotonic) clock.
+  ///
+  /// You may cancel this Future to reclaim resources.
+  ///
+  /// This future probably completes on the timer thread. You should almost
+  /// certainly follow it with a via() call or the accuracy of other timers
+  /// will suffer.
+  virtual Future<Unit> after(Duration) = 0;
+
+  /// Returns a future that will complete at the requested time.
+  ///
+  /// You may cancel this Future to reclaim resources.
+  ///
+  /// NB This is sugar for `after(when - now)`, so while you are welcome to
+  /// use a std::chrono::system_clock::time_point it will not track changes to
+  /// the system clock but rather execute that many milliseconds in the future
+  /// according to the steady clock.
+  template <class Clock>
+  Future<Unit> at(std::chrono::time_point<Clock> when);
+};
+
+} // namespace folly
+
+// now get those definitions
+#include <folly/futures/Future.h>
+
+// finally we can use Future
+namespace folly {
+
+template <class Clock>
+Future<Unit> Timekeeper::at(std::chrono::time_point<Clock> when) {
+  auto now = Clock::now();
+
+  if (when <= now) {
+    return makeFuture();
+  }
+
+  return after(std::chrono::duration_cast<Duration>(when - now));
+}
+
+} // namespace folly
diff --git a/faux-folly/folly/futures/Try-inl.h b/faux-folly/folly/futures/Try-inl.h
new file mode 100644
index 0000000..05f384a
--- /dev/null
+++ b/faux-folly/folly/futures/Try-inl.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2015 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 <stdexcept>
+
+#include <folly/futures/FutureException.h>
+
+namespace folly {
+
+template <class T>
+Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) {
+  if (contains_ == Contains::VALUE) {
+    new (&value_)T(std::move(t.value_));
+  } else if (contains_ == Contains::EXCEPTION) {
+    new (&e_)std::unique_ptr<exception_wrapper>(std::move(t.e_));
+  }
+}
+
+template <class T>
+template <class T2>
+Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value,
+                                    Try<void> const&>::type t)
+    : contains_(Contains::NOTHING) {
+  if (t.hasValue()) {
+    contains_ = Contains::VALUE;
+    new (&value_) T();
+  } else if (t.hasException()) {
+    contains_ = Contains::EXCEPTION;
+    new (&e_) std::unique_ptr<exception_wrapper>(
+        folly::make_unique<exception_wrapper>(t.exception()));
+  }
+}
+
+template <class T>
+Try<T>& Try<T>::operator=(Try<T>&& t) noexcept {
+  if (this == &t) {
+    return *this;
+  }
+
+  this->~Try();
+  contains_ = t.contains_;
+  if (contains_ == Contains::VALUE) {
+    new (&value_)T(std::move(t.value_));
+  } else if (contains_ == Contains::EXCEPTION) {
+    new (&e_)std::unique_ptr<exception_wrapper>(std::move(t.e_));
+  }
+  return *this;
+}
+
+template <class T>
+Try<T>::Try(const Try<T>& t) {
+  static_assert(
+      std::is_copy_constructible<T>::value,
+      "T must be copyable for Try<T> to be copyable");
+  contains_ = t.contains_;
+  if (contains_ == Contains::VALUE) {
+    new (&value_)T(t.value_);
+  } else if (contains_ == Contains::EXCEPTION) {
+    new (&e_)std::unique_ptr<exception_wrapper>();
+    e_ = folly::make_unique<exception_wrapper>(*(t.e_));
+  }
+}
+
+template <class T>
+Try<T>& Try<T>::operator=(const Try<T>& t) {
+  static_assert(
+      std::is_copy_constructible<T>::value,
+      "T must be copyable for Try<T> to be copyable");
+  this->~Try();
+  contains_ = t.contains_;
+  if (contains_ == Contains::VALUE) {
+    new (&value_)T(t.value_);
+  } else if (contains_ == Contains::EXCEPTION) {
+    new (&e_)std::unique_ptr<exception_wrapper>();
+    e_ = folly::make_unique<exception_wrapper>(*(t.e_));
+  }
+  return *this;
+}
+
+template <class T>
+Try<T>::~Try() {
+  if (LIKELY(contains_ == Contains::VALUE)) {
+    value_.~T();
+  } else if (UNLIKELY(contains_ == Contains::EXCEPTION)) {
+    e_.~unique_ptr<exception_wrapper>();
+  }
+}
+
+template <class T>
+T& Try<T>::value() {
+  throwIfFailed();
+  return value_;
+}
+
+template <class T>
+const T& Try<T>::value() const {
+  throwIfFailed();
+  return value_;
+}
+
+template <class T>
+void Try<T>::throwIfFailed() const {
+  if (contains_ != Contains::VALUE) {
+    if (contains_ == Contains::EXCEPTION) {
+      e_->throwException();
+    } else {
+      throw UsingUninitializedTry();
+    }
+  }
+}
+
+void Try<void>::throwIfFailed() const {
+  if (!hasValue_) {
+    e_->throwException();
+  }
+}
+
+template <typename T>
+inline T moveFromTry(Try<T>&& t) {
+  return std::move(t.value());
+}
+
+inline void moveFromTry(Try<void>&& t) {
+  return t.value();
+}
+
+template <typename F>
+typename std::enable_if<
+  !std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<typename std::result_of<F()>::type>>::type
+makeTryWith(F&& f) {
+  typedef typename std::result_of<F()>::type ResultType;
+  try {
+    return Try<ResultType>(f());
+  } catch (std::exception& e) {
+    return Try<ResultType>(exception_wrapper(std::current_exception(), e));
+  } catch (...) {
+    return Try<ResultType>(exception_wrapper(std::current_exception()));
+  }
+}
+
+template <typename F>
+typename std::enable_if<
+  std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<void>>::type
+makeTryWith(F&& f) {
+  try {
+    f();
+    return Try<void>();
+  } catch (std::exception& e) {
+    return Try<void>(exception_wrapper(std::current_exception(), e));
+  } catch (...) {
+    return Try<void>(exception_wrapper(std::current_exception()));
+  }
+}
+
+} // folly
diff --git a/faux-folly/folly/futures/Try.h b/faux-folly/folly/futures/Try.h
new file mode 100644
index 0000000..e5e38af
--- /dev/null
+++ b/faux-folly/folly/futures/Try.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2015 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 <type_traits>
+#include <exception>
+#include <algorithm>
+#include <folly/ExceptionWrapper.h>
+#include <folly/Likely.h>
+#include <folly/Memory.h>
+#include <folly/Portability.h>
+#include <folly/futures/FutureException.h>
+#include <folly/futures/Unit.h>
+
+namespace folly {
+
+/*
+ * Try<T> is a wrapper that contains either an instance of T, an exception, or
+ * nothing. Its primary use case is as a representation of a Promise or Future's
+ * result and so it provides a number of methods that are useful in that
+ * context. Exceptions are stored as exception_wrappers so that the user can
+ * minimize rethrows if so desired.
+ *
+ * To represent success or a captured exception, use Try<Unit>
+ */
+template <class T>
+class Try {
+  static_assert(!std::is_reference<T>::value,
+                "Try may not be used with reference types");
+
+  enum class Contains {
+    VALUE,
+    EXCEPTION,
+    NOTHING,
+  };
+
+ public:
+  /*
+   * The value type for the Try
+   */
+  typedef T element_type;
+
+  /*
+   * Construct an empty Try
+   */
+  Try() : contains_(Contains::NOTHING) {}
+
+  /*
+   * Construct a Try with a value by copy
+   *
+   * @param v The value to copy in
+   */
+  explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
+
+  /*
+   * Construct a Try with a value by move
+   *
+   * @param v The value to move in
+   */
+  explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
+
+  /// Implicit conversion from Try<void> to Try<Unit>
+  template <class T2 = T>
+  /* implicit */
+  Try(typename std::enable_if<std::is_same<Unit, T2>::value,
+                              Try<void> const&>::type t);
+
+  /*
+   * Construct a Try with an exception_wrapper
+   *
+   * @param e The exception_wrapper
+   */
+  explicit Try(exception_wrapper e)
+    : contains_(Contains::EXCEPTION),
+      e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
+
+  /*
+   * DEPRECATED
+   * Construct a Try with an exception_pointer
+   *
+   * @param ep The exception_pointer. Will be rethrown.
+   */
+  FOLLY_DEPRECATED("use Try(exception_wrapper)")
+  explicit Try(std::exception_ptr ep)
+    : contains_(Contains::EXCEPTION) {
+    try {
+      std::rethrow_exception(ep);
+    } catch (const std::exception& e) {
+      e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
+    } catch (...) {
+      e_ = folly::make_unique<exception_wrapper>(std::current_exception());
+    }
+  }
+
+  // Move constructor
+  Try(Try<T>&& t) noexcept;
+  // Move assigner
+  Try& operator=(Try<T>&& t) noexcept;
+
+  // Copy constructor
+  Try(const Try& t);
+  // Copy assigner
+  Try& operator=(const Try& t);
+
+  ~Try();
+
+  /*
+   * Get a mutable reference to the contained value. If the Try contains an
+   * exception it will be rethrown.
+   *
+   * @returns mutable reference to the contained value
+   */
+  T& value();
+  /*
+   * Get a const reference to the contained value. If the Try contains an
+   * exception it will be rethrown.
+   *
+   * @returns const reference to the contained value
+   */
+  const T& value() const;
+
+  /*
+   * If the Try contains an exception, rethrow it. Otherwise do nothing.
+   */
+  void throwIfFailed() const;
+
+  /*
+   * Const dereference operator. If the Try contains an exception it will be
+   * rethrown.
+   *
+   * @returns const reference to the contained value
+   */
+  const T& operator*() const { return value(); }
+  /*
+   * Dereference operator. If the Try contains an exception it will be rethrown.
+   *
+   * @returns mutable reference to the contained value
+   */
+  T& operator*() { return value(); }
+
+  /*
+   * Const arrow operator. If the Try contains an exception it will be
+   * rethrown.
+   *
+   * @returns const reference to the contained value
+   */
+  const T* operator->() const { return &value(); }
+  /*
+   * Arrow operator. If the Try contains an exception it will be rethrown.
+   *
+   * @returns mutable reference to the contained value
+   */
+  T* operator->() { return &value(); }
+
+  /*
+   * @returns True if the Try contains a value, false otherwise
+   */
+  bool hasValue() const { return contains_ == Contains::VALUE; }
+  /*
+   * @returns True if the Try contains an exception, false otherwise
+   */
+  bool hasException() const { return contains_ == Contains::EXCEPTION; }
+
+  /*
+   * @returns True if the Try contains an exception of type Ex, false otherwise
+   */
+  template <class Ex>
+  bool hasException() const {
+    return hasException() && e_->is_compatible_with<Ex>();
+  }
+
+  exception_wrapper& exception() {
+    if (UNLIKELY(!hasException())) {
+      throw FutureException("exception(): Try does not contain an exception");
+    }
+    return *e_;
+  }
+
+  const exception_wrapper& exception() const {
+    if (UNLIKELY(!hasException())) {
+      throw FutureException("exception(): Try does not contain an exception");
+    }
+    return *e_;
+  }
+
+  /*
+   * If the Try contains an exception and it is of type Ex, execute func(Ex)
+   *
+   * @param func a function that takes a single parameter of type const Ex&
+   *
+   * @returns True if the Try held an Ex and func was executed, false otherwise
+   */
+  template <class Ex, class F>
+  bool withException(F func) const {
+    if (!hasException()) {
+      return false;
+    }
+    return e_->with_exception<Ex>(std::move(func));
+  }
+
+  template <bool isTry, typename R>
+  typename std::enable_if<isTry, R>::type get() {
+    return std::forward<R>(*this);
+  }
+
+  template <bool isTry, typename R>
+  typename std::enable_if<!isTry, R>::type get() {
+    return std::forward<R>(value());
+  }
+
+ private:
+  Contains contains_;
+  union {
+    T value_;
+    std::unique_ptr<exception_wrapper> e_;
+  };
+};
+
+/*
+ * Specialization of Try for void value type. Encapsulates either success or an
+ * exception.
+ */
+template <>
+class Try<void> {
+ public:
+  // Construct a Try holding a successful and void result
+  Try() : hasValue_(true) {}
+
+  /*
+   * Construct a Try with an exception_wrapper
+   *
+   * @param e The exception_wrapper
+   */
+  explicit Try(exception_wrapper e)
+    : hasValue_(false),
+      e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
+
+  /*
+   * DEPRECATED
+   * Construct a Try with an exception_pointer
+   *
+   * @param ep The exception_pointer. Will be rethrown.
+   */
+  FOLLY_DEPRECATED("use Try(exception_wrapper)")
+  explicit Try(std::exception_ptr ep) : hasValue_(false) {
+    try {
+      std::rethrow_exception(ep);
+    } catch (const std::exception& e) {
+      e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
+    } catch (...) {
+      e_ = folly::make_unique<exception_wrapper>(std::current_exception());
+    }
+  }
+
+  // Copy assigner
+  Try& operator=(const Try<void>& t) {
+    hasValue_ = t.hasValue_;
+    if (t.e_) {
+      e_ = folly::make_unique<exception_wrapper>(*t.e_);
+    }
+    return *this;
+  }
+  // Copy constructor
+  Try(const Try<void>& t) {
+    *this = t;
+  }
+
+  // If the Try contains an exception, throws it
+  void value() const { throwIfFailed(); }
+  // Dereference operator. If the Try contains an exception, throws it
+  void operator*() const { return value(); }
+
+  // If the Try contains an exception, throws it
+  inline void throwIfFailed() const;
+
+  // @returns False if the Try contains an exception, true otherwise
+  bool hasValue() const { return hasValue_; }
+  // @returns True if the Try contains an exception, false otherwise
+  bool hasException() const { return !hasValue_; }
+
+  // @returns True if the Try contains an exception of type Ex, false otherwise
+  template <class Ex>
+  bool hasException() const {
+    return hasException() && e_->is_compatible_with<Ex>();
+  }
+
+  /*
+   * @throws FutureException if the Try doesn't contain an exception
+   *
+   * @returns mutable reference to the exception contained by this Try
+   */
+  exception_wrapper& exception() {
+    if (UNLIKELY(!hasException())) {
+      throw FutureException("exception(): Try does not contain an exception");
+    }
+    return *e_;
+  }
+
+  const exception_wrapper& exception() const {
+    if (UNLIKELY(!hasException())) {
+      throw FutureException("exception(): Try does not contain an exception");
+    }
+    return *e_;
+  }
+
+  /*
+   * If the Try contains an exception and it is of type Ex, execute func(Ex)
+   *
+   * @param func a function that takes a single parameter of type const Ex&
+   *
+   * @returns True if the Try held an Ex and func was executed, false otherwise
+   */
+  template <class Ex, class F>
+  bool withException(F func) const {
+    if (!hasException()) {
+      return false;
+    }
+    return e_->with_exception<Ex>(std::move(func));
+  }
+
+  template <bool, typename R>
+  R get() {
+    return std::forward<R>(*this);
+  }
+
+ private:
+  bool hasValue_;
+  std::unique_ptr<exception_wrapper> e_{nullptr};
+};
+
+/*
+ * Extracts value from try and returns it. Throws if try contained an exception.
+ *
+ * @param t Try to extract value from
+ *
+ * @returns value contained in t
+ */
+template <typename T>
+T moveFromTry(Try<T>&& t);
+
+/*
+ * Throws if try contained an exception.
+ *
+ * @param t Try to move from
+ */
+void moveFromTry(Try<void>&& t);
+
+/*
+ * @param f a function to execute and capture the result of (value or exception)
+ *
+ * @returns Try holding the result of f
+ */
+template <typename F>
+typename std::enable_if<
+  !std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<typename std::result_of<F()>::type>>::type
+makeTryWith(F&& f);
+
+/*
+ * Specialization of makeTryWith for void return
+ *
+ * @param f a function to execute and capture the result of
+ *
+ * @returns Try<void> holding the result of f
+ */
+template <typename F>
+typename std::enable_if<
+  std::is_same<typename std::result_of<F()>::type, void>::value,
+  Try<void>>::type
+makeTryWith(F&& f);
+
+} // folly
+
+#include <folly/futures/Try-inl.h>
diff --git a/faux-folly/folly/futures/Unit.h b/faux-folly/folly/futures/Unit.h
new file mode 100644
index 0000000..521b206
--- /dev/null
+++ b/faux-folly/folly/futures/Unit.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 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
+namespace folly {
+
+/// In functional programming, the degenerate case is often called "unit". In
+/// C++, "void" is often the best analogue, however because of the syntactic
+/// special-casing required for void it is a liability for template
+/// metaprogramming. So, instead of e.g. Future<void>, we have Future<Unit>.
+/// You can ignore the actual value, and we port some of the syntactic
+/// niceties like setValue() instead of setValue(Unit{}).
+struct Unit {
+  /// Lift type T into Unit. This is the definition for all non-void types.
+  template <class T> struct Lift : public std::false_type {
+    using type = T;
+  };
+  template <class T> struct Drop : public std::false_type {
+    using type = T;
+  };
+  bool operator==(const Unit& /*other*/) const { return true; }
+  bool operator!=(const Unit& /*other*/) const { return false; }
+};
+
+// Lift void into Unit.
+template <>
+struct Unit::Lift<void> : public std::true_type {
+  using type = Unit;
+};
+
+// Lift Unit into Unit (identity).
+template <>
+struct Unit::Lift<Unit> : public std::true_type {
+  using type = Unit;
+};
+
+// Drop Unit into void.
+template <>
+struct Unit::Drop<Unit> : public std::true_type {
+  using type = void;
+};
+
+// Drop void into void (identity).
+template <>
+struct Unit::Drop<void> : public std::true_type {
+  using type = void;
+};
+
+template <class T>
+struct is_void_or_unit : public Unit::Lift<T>
+{};
+
+}
diff --git a/faux-folly/folly/futures/detail/Core.h b/faux-folly/folly/futures/detail/Core.h
new file mode 100644
index 0000000..9be1720
--- /dev/null
+++ b/faux-folly/folly/futures/detail/Core.h
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <mutex>
+#include <stdexcept>
+#include <vector>
+
+#include <folly/Optional.h>
+#include <folly/MicroSpinLock.h>
+#include <folly/Cancellation.h>
+
+#include <folly/futures/Try.h>
+#include <folly/futures/Promise.h>
+#include <folly/futures/Future.h>
+#include <folly/Executor.h>
+#include <folly/futures/detail/FSM.h>
+
+namespace folly { namespace detail {
+
+/*
+        OnlyCallback
+       /            \
+  Start              Armed - Done
+       \            /
+         OnlyResult
+
+This state machine is fairly self-explanatory. The most important bit is
+that the callback is only executed on the transition from Armed to Done,
+and that transition can happen immediately after transitioning from Only*
+to Armed, if it is active (the usual case).
+*/
+enum class State : uint8_t {
+  Start,
+  OnlyResult,
+  OnlyCallback,
+  Armed,
+  Done,
+};
+
+/// The shared state object for Future and Promise.
+/// Some methods must only be called by either the Future thread or the
+/// Promise thread. The Future thread is the thread that currently "owns" the
+/// Future and its callback-related operations, and the Promise thread is
+/// likewise the thread that currently "owns" the Promise and its
+/// result-related operations. Also, Futures own interruption, Promises own
+/// interrupt handlers. Unfortunately, there are things that users can do to
+/// break this, and we can't detect that. However if they follow move
+/// semantics religiously wrt threading, they should be ok.
+///
+/// It's worth pointing out that Futures and/or Promises can and usually will
+/// migrate between threads, though this usually happens within the API code.
+/// For example, an async operation will probably make a Promise, grab its
+/// Future, then move the Promise into another thread that will eventually
+/// fulfill it. With executors and via, this gets slightly more complicated at
+/// first blush, but it's the same principle. In general, as long as the user
+/// doesn't access a Future or Promise object from more than one thread at a
+/// time there won't be any problems.
+template<typename T>
+class Core {
+  static_assert(!std::is_void<T>::value,
+                "void futures are not supported. Use Unit instead.");
+ public:
+  /// This must be heap-constructed. There's probably a way to enforce that in
+  /// code but since this is just internal detail code and I don't know how
+  /// off-hand, I'm punting.
+  Core() : result_(), fsm_(State::Start), attached_(2) {}
+
+  explicit Core(Try<T>&& t)
+    : result_(std::move(t)),
+      fsm_(State::OnlyResult),
+      attached_(1) {}
+
+  ~Core() {
+    DCHECK(attached_ == 0);
+  }
+
+  // not copyable
+  Core(Core const&) = delete;
+  Core& operator=(Core const&) = delete;
+
+  // not movable (see comment in the implementation of Future::then)
+  Core(Core&&) noexcept = delete;
+  Core& operator=(Core&&) = delete;
+
+  /// May call from any thread
+  bool hasResult() const {
+    switch (fsm_.getState()) {
+      case State::OnlyResult:
+      case State::Armed:
+      case State::Done:
+        assert(!!result_);
+        return true;
+
+      default:
+        return false;
+    }
+  }
+
+  /// May call from any thread
+  bool ready() const {
+    return hasResult();
+  }
+
+  /// May call from any thread
+  Try<T>& getTry() {
+    if (ready()) {
+      return *result_;
+    } else {
+      throw FutureNotReady();
+    }
+  }
+
+  template <typename F>
+  class LambdaBufHelper {
+   public:
+    template <typename FF>
+    explicit LambdaBufHelper(FF&& func) : func_(std::forward<FF>(func)) {}
+    void operator()(Try<T>&& t) {
+      SCOPE_EXIT { this->~LambdaBufHelper(); };
+      func_(std::move(t));
+    }
+   private:
+    F func_;
+  };
+
+  /// Call only from Future thread.
+  template <typename F>
+  void setCallback(F func) {
+    bool transitionToArmed = false;
+    auto setCallback_ = [&]{
+      // Move the lambda into the Core if it fits
+      if (sizeof(LambdaBufHelper<F>) <= lambdaBufSize) {
+        auto funcLoc = reinterpret_cast<LambdaBufHelper<F>*>(&lambdaBuf_);
+        new (funcLoc) LambdaBufHelper<F>(std::forward<F>(func));
+        callback_ = std::ref(*funcLoc);
+      } else {
+        callback_ = std::move(func);
+      }
+    };
+
+    FSM_START(fsm_)
+      case State::Start:
+        FSM_UPDATE(fsm_, State::OnlyCallback, setCallback_);
+        break;
+
+      case State::OnlyResult:
+        FSM_UPDATE(fsm_, State::Armed, setCallback_);
+        transitionToArmed = true;
+        break;
+
+      case State::OnlyCallback:
+      case State::Armed:
+      case State::Done:
+        throw std::logic_error("setCallback called twice");
+    FSM_END
+
+    // we could always call this, it is an optimization to only call it when
+    // it might be needed.
+    if (transitionToArmed) {
+      maybeCallback();
+    }
+  }
+
+  /// Call only from Promise thread
+  void setResult(Try<T>&& t) {
+    bool transitionToArmed = false;
+    auto setResult_ = [&]{ result_ = std::move(t); };
+    FSM_START(fsm_)
+      case State::Start:
+        FSM_UPDATE(fsm_, State::OnlyResult, setResult_);
+        break;
+
+      case State::OnlyCallback:
+        FSM_UPDATE(fsm_, State::Armed, setResult_);
+        transitionToArmed = true;
+        break;
+
+      case State::OnlyResult:
+      case State::Armed:
+      case State::Done:
+        throw std::logic_error("setResult called twice");
+    FSM_END
+
+    if (transitionToArmed) {
+      maybeCallback();
+    }
+  }
+
+  /// Called by a destructing Future (in the Future thread, by definition)
+  void detachFuture() {
+    activate();
+    detachOne();
+  }
+
+  /// Called by a destructing Promise (in the Promise thread, by definition)
+  void detachPromise() {
+    // detachPromise() and setResult() should never be called in parallel
+    // so we don't need to protect this.
+    if (UNLIKELY(!result_)) {
+      setResult(Try<T>(exception_wrapper(BrokenPromise())));
+    }
+    detachOne();
+  }
+
+  /// May call from any thread
+  void deactivate() {
+    active_.store(false, std::memory_order_release);
+  }
+
+  /// May call from any thread
+  void activate() {
+    active_.store(true, std::memory_order_release);
+    maybeCallback();
+  }
+
+  /// May call from any thread
+  bool isActive() { return active_.load(std::memory_order_acquire); }
+
+  /// Call only from Future thread
+  void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
+    if (!executorLock_.try_lock()) {
+      executorLock_.lock();
+    }
+    executor_ = x;
+    priority_ = priority;
+    executorLock_.unlock();
+  }
+
+  void setExecutorNoLock(Executor* x, int8_t priority = Executor::MID_PRI) {
+    executor_ = x;
+    priority_ = priority;
+  }
+
+  void setCancellation(Cancellation cx) {
+    if (!executorLock_.try_lock()) {
+      executorLock_.lock();
+    }
+    cancellation_ = std::move(cx);
+    executorLock_.unlock();
+  }
+
+  Cancellation getCancellation() {
+    if (!executorLock_.try_lock()) {
+      executorLock_.lock();
+    }
+    auto cx = cancellation_;
+    executorLock_.unlock();
+    return cx;
+  }
+
+
+  Executor* getExecutor() {
+    return executor_;
+  }
+
+  /// Call only from Future thread
+  void raise(exception_wrapper e) {
+    if (!interruptLock_.try_lock()) {
+      interruptLock_.lock();
+    }
+    if (!interrupt_ && !hasResult()) {
+      interrupt_ = folly::make_unique<exception_wrapper>(std::move(e));
+      if (interruptHandler_) {
+        interruptHandler_(*interrupt_);
+      }
+    }
+    interruptLock_.unlock();
+  }
+
+  std::function<void(exception_wrapper const&)> getInterruptHandler() {
+    if (!interruptHandlerSet_.load(std::memory_order_acquire)) {
+      return nullptr;
+    }
+    if (!interruptLock_.try_lock()) {
+      interruptLock_.lock();
+    }
+    auto handler = interruptHandler_;
+    interruptLock_.unlock();
+    return handler;
+  }
+
+  /// Call only from Promise thread
+  void setInterruptHandler(std::function<void(exception_wrapper const&)> fn) {
+    if (!interruptLock_.try_lock()) {
+      interruptLock_.lock();
+    }
+    if (!hasResult()) {
+      if (interrupt_) {
+        fn(*interrupt_);
+      } else {
+        setInterruptHandlerNoLock(std::move(fn));
+      }
+    }
+    interruptLock_.unlock();
+  }
+
+  void setInterruptHandlerNoLock(
+      std::function<void(exception_wrapper const&)> fn) {
+    interruptHandlerSet_.store(true, std::memory_order_relaxed);
+    interruptHandler_ = std::move(fn);
+  }
+
+ protected:
+  void maybeCallback() {
+    FSM_START(fsm_)
+      case State::Armed:
+        if (active_.load(std::memory_order_acquire)) {
+          FSM_UPDATE2(fsm_, State::Done, []{}, [this]{ this->doCallback(); });
+        }
+        FSM_BREAK
+
+      default:
+        FSM_BREAK
+    FSM_END
+  }
+
+  void doCallback() {
+    Executor* x = executor_;
+    int8_t priority;
+    Cancellation cx;
+    if (x) {
+      if (!executorLock_.try_lock()) {
+        executorLock_.lock();
+      }
+      x = executor_;
+      priority = priority_;
+      cx = cancellation_;
+      executorLock_.unlock();
+    }
+
+    auto cxhold = cx.hold_state();
+
+    if (!cxhold)
+        return;
+
+    if (x) {
+      // keep Core alive until executor did its thing
+      ++attached_;
+      try {
+        if (LIKELY(x->getNumPriorities() == 1)) {
+          x->add([this, cx]() mutable {
+            SCOPE_EXIT { detachOne(); };
+            auto hold = cx.hold_state();
+            if (!hold) return;
+            callback_(std::move(*result_));
+          });
+        } else {
+          x->addWithPriority([this, cx]() mutable {
+            SCOPE_EXIT { detachOne(); };
+            auto hold = cx.hold_state();
+            if (!hold) return;
+            callback_(std::move(*result_));
+          }, priority);
+        }
+      } catch (...) {
+        --attached_; // Account for extra ++attached_ before try
+        result_ = Try<T>(exception_wrapper(std::current_exception()));
+        callback_(std::move(*result_));
+      }
+    } else {
+      callback_(std::move(*result_));
+    }
+  }
+
+  void detachOne() {
+    auto a = --attached_;
+    assert(a >= 0);
+    assert(a <= 2);
+    if (a == 0) {
+      delete this;
+    }
+  }
+
+  // lambdaBuf occupies exactly one cache line
+  static constexpr size_t lambdaBufSize = 8 * sizeof(void*);
+  typename std::aligned_storage<lambdaBufSize>::type lambdaBuf_;
+  // place result_ next to increase the likelihood that the value will be
+  // contained entirely in one cache line
+  folly::Optional<Try<T>> result_;
+  std::function<void(Try<T>&&)> callback_ {nullptr};
+  FSM<State> fsm_;
+  std::atomic<unsigned char> attached_;
+  std::atomic<bool> active_ {true};
+  std::atomic<bool> interruptHandlerSet_ {false};
+  folly::MicroSpinLock interruptLock_ {0};
+  folly::MicroSpinLock executorLock_ {0};
+  int8_t priority_ {-1};
+  Executor* executor_ {nullptr};
+  Cancellation cancellation_ {}; /* protect using executorLock_ */
+  std::unique_ptr<exception_wrapper> interrupt_ {};
+  std::function<void(exception_wrapper const&)> interruptHandler_ {nullptr};
+};
+
+template <typename... Ts>
+struct CollectAllVariadicContext {
+  CollectAllVariadicContext() {}
+  template <typename T, size_t I>
+  inline void setPartialResult(Try<T>& t) {
+    std::get<I>(results) = std::move(t);
+  }
+  ~CollectAllVariadicContext() {
+    p.setValue(std::move(results));
+  }
+  Promise<std::tuple<Try<Ts>...>> p;
+  std::tuple<Try<Ts>...> results;
+  typedef Future<std::tuple<Try<Ts>...>> type;
+};
+
+template <typename... Ts>
+struct CollectVariadicContext {
+  CollectVariadicContext() {}
+  template <typename T, size_t I>
+  inline void setPartialResult(Try<T>& t) {
+    if (t.hasException()) {
+       if (!threw.exchange(true)) {
+         p.setException(std::move(t.exception()));
+       }
+     } else if (!threw) {
+       std::get<I>(results) = std::move(t.value());
+     }
+  }
+  ~CollectVariadicContext() {
+    if (!threw.exchange(true)) {
+      p.setValue(std::move(results));
+    }
+  }
+  Promise<std::tuple<Ts...>> p;
+  std::tuple<Ts...> results;
+  std::atomic<bool> threw {false};
+  typedef Future<std::tuple<Ts...>> type;
+};
+
+template <template <typename ...> class T, typename... Ts>
+void collectVariadicHelper(const std::shared_ptr<T<Ts...>>& ctx) {
+  // base case
+}
+
+template <template <typename ...> class T, typename... Ts,
+          typename THead, typename... TTail>
+void collectVariadicHelper(const std::shared_ptr<T<Ts...>>& ctx,
+                           THead&& head, TTail&&... tail) {
+  head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
+    ctx->template setPartialResult<typename THead::value_type,
+                                   sizeof...(Ts) - sizeof...(TTail) - 1>(t);
+  });
+  // template tail-recursion
+  collectVariadicHelper(ctx, std::forward<TTail>(tail)...);
+}
+
+}} // folly::detail
diff --git a/faux-folly/folly/futures/detail/FSM.h b/faux-folly/folly/futures/detail/FSM.h
new file mode 100644
index 0000000..b6c7866
--- /dev/null
+++ b/faux-folly/folly/futures/detail/FSM.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <mutex>
+#include <folly/MicroSpinLock.h>
+
+namespace folly { namespace detail {
+
+/// Finite State Machine helper base class.
+/// Inherit from this.
+/// For best results, use an "enum class" for Enum.
+template <class Enum>
+class FSM {
+private:
+  // I am not templatizing this because folly::MicroSpinLock needs to be
+  // zero-initialized (or call init) which isn't generic enough for something
+  // that behaves like std::mutex. :(
+  using Mutex = folly::MicroSpinLock;
+  Mutex mutex_ {0};
+
+  // This might not be necessary for all Enum types, e.g. anything
+  // that is atomically updated in practice on this CPU and there's no risk
+  // of returning a bogus state because of tearing.
+  // An optimization would be to use a static conditional on the Enum type.
+  std::atomic<Enum> state_;
+
+public:
+  explicit FSM(Enum startState) : state_(startState) {}
+
+  Enum getState() const {
+    return state_.load(std::memory_order_acquire);
+  }
+
+  /// Atomically do a state transition with accompanying action.
+  /// The action will see the old state.
+  /// @returns true on success, false and action unexecuted otherwise
+  template <class F>
+  bool updateState(Enum A, Enum B, F const& action) {
+    if (!mutex_.try_lock()) {
+      mutex_.lock();
+    }
+    if (state_.load(std::memory_order_acquire) != A) {
+      mutex_.unlock();
+      return false;
+    }
+    action();
+    state_.store(B, std::memory_order_release);
+    mutex_.unlock();
+    return true;
+  }
+
+  /// Atomically do a state transition with accompanying action. Then do the
+  /// unprotected action without holding the lock. If the atomic transition
+  /// fails, returns false and neither action was executed.
+  ///
+  /// This facilitates code like this:
+  ///   bool done = false;
+  ///   while (!done) {
+  ///     switch (getState()) {
+  ///     case State::Foo:
+  ///       done = updateState(State::Foo, State::Bar,
+  ///           [&]{ /* do protected stuff */ },
+  ///           [&]{ /* do unprotected stuff */});
+  ///       break;
+  ///
+  /// Which reads nicer than code like this:
+  ///   while (true) {
+  ///     switch (getState()) {
+  ///     case State::Foo:
+  ///       if (!updateState(State::Foo, State::Bar,
+  ///           [&]{ /* do protected stuff */ })) {
+  ///         continue;
+  ///       }
+  ///       /* do unprotected stuff */
+  ///       return; // or otherwise break out of the loop
+  ///
+  /// The protected action will see the old state, and the unprotected action
+  /// will see the new state.
+  template <class F1, class F2>
+  bool updateState(Enum A, Enum B,
+                   F1 const& protectedAction, F2 const& unprotectedAction) {
+    bool result = updateState(A, B, protectedAction);
+    if (result) {
+      unprotectedAction();
+    }
+    return result;
+  }
+};
+
+#define FSM_START(fsm) {\
+    bool done = false; \
+    while (!done) { auto state = fsm.getState(); switch (state) {
+
+#define FSM_UPDATE2(fsm, b, protectedAction, unprotectedAction) \
+    done = fsm.updateState(state, (b), (protectedAction), (unprotectedAction));
+
+#define FSM_UPDATE(fsm, b, action) FSM_UPDATE2(fsm, (b), (action), []{})
+
+#define FSM_CASE(fsm, a, b, action) \
+  case (a): \
+    FSM_UPDATE(fsm, (b), (action)); \
+    break;
+
+#define FSM_CASE2(fsm, a, b, protectedAction, unprotectedAction) \
+  case (a): \
+    FSM_UPDATE2(fsm, (b), (protectedAction), (unprotectedAction)); \
+    break;
+
+#define FSM_BREAK done = true; break;
+#define FSM_END }}}
+
+
+}} // folly::detail
diff --git a/faux-folly/folly/futures/detail/ThreadWheelTimekeeper.cpp b/faux-folly/folly/futures/detail/ThreadWheelTimekeeper.cpp
new file mode 100644
index 0000000..c030ce0
--- /dev/null
+++ b/faux-folly/folly/futures/detail/ThreadWheelTimekeeper.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2015 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 "ThreadWheelTimekeeper.h"
+
+#include <folly/futures/Future.h>
+#include <future>
+#include <memory>
+#include <vector>
+
+namespace folly { namespace detail {
+
+using std::vector;
+
+namespace {
+  using lock_guard = std::lock_guard<std::mutex>;
+  using unique_lock = std::unique_lock<std::mutex>;
+  using std::make_shared;
+
+  std::mutex g_timekeeperSingletonCreateionMutex_;
+  ThreadWheelTimekeeper *g_timekeeperSingleton_{};
+} // namespace
+
+
+ThreadWheelTimekeeper::ThreadWheelTimekeeper() :
+    timerMap_(std::chrono::milliseconds(1)),
+    thread_([this]{ thread_run(); })
+{
+}
+
+ThreadWheelTimekeeper::~ThreadWheelTimekeeper() {
+    unique_lock lock(mutex_);
+    running_ = false;
+    cond_.notify_one();
+    lock.unlock();
+
+    thread_.join();
+}
+
+void ThreadWheelTimekeeper::thread_run()
+{
+    unique_lock lock(mutex_);
+
+    while (running_) {
+        if (timerMap_.empty()) {
+            cond_.wait(lock);
+            continue;
+        }
+
+        auto tp = timerMap_.nextTimeout();
+        cond_.wait_until(lock, tp);
+
+        vector<TimerMap::data_type> vec;
+        timerMap_.popExpiredTimers(std::inserter(vec, vec.begin()));
+        lock.unlock();
+        TimerMap::invokeTimers(vec.begin(), vec.end());
+        lock.lock();
+    }
+}
+
+Future<Unit> ThreadWheelTimekeeper::after(Duration dur) {
+    // first: the promise, second: a cancellation
+    using two_type = std::pair< Promise<Unit>, unsigned >;
+    auto twoptr = make_shared<two_type>(Promise<Unit>(), 0);
+    auto& prom = twoptr->first;
+    auto& cx = twoptr->second;
+
+    prom.setInterruptHandler([twoptr, &prom, &cx] (exception_wrapper const& w) {
+            cx = 1;
+            prom.setException(w);
+        });
+
+    auto f = prom.getFuture();
+
+    lock_guard lock(mutex_);
+    timerMap_.scheduleTimeout(dur, [twoptr, &prom, &cx] () {
+            if (cx) {
+                return;
+            }
+            prom.setValue();
+        });
+    cond_.notify_one();
+
+    return f;
+}
+
+Timekeeper* getTimekeeperSingleton() {
+    if (!g_timekeeperSingleton_)
+    {
+        lock_guard lock{g_timekeeperSingletonCreateionMutex_};
+        if (!g_timekeeperSingleton_)
+        {
+            g_timekeeperSingleton_ = new ThreadWheelTimekeeper;
+        }
+    }
+
+    return g_timekeeperSingleton_;
+}
+
+}} // folly::detail
diff --git a/faux-folly/folly/futures/detail/ThreadWheelTimekeeper.h b/faux-folly/folly/futures/detail/ThreadWheelTimekeeper.h
new file mode 100644
index 0000000..995b2e6
--- /dev/null
+++ b/faux-folly/folly/futures/detail/ThreadWheelTimekeeper.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 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/futures/Future.h>
+#include <folly/futures/Timekeeper.h>
+#include <folly/futures/detail/TimerMap.h>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+namespace folly { namespace detail {
+
+/// The default Timekeeper implementation which uses a HHWheelTimer on an
+/// EventBase in a dedicated thread. Users needn't deal with this directly, it
+/// is used by default by Future methods that work with timeouts.
+class ThreadWheelTimekeeper : public Timekeeper {
+ public:
+  /// But it doesn't *have* to be a singleton.
+  ThreadWheelTimekeeper();
+  ~ThreadWheelTimekeeper() override;
+
+  /// Implement the Timekeeper interface
+  /// This future *does* complete on the timer thread. You should almost
+  /// certainly follow it with a via() call or the accuracy of other timers
+  /// will suffer.
+  Future<Unit> after(Duration) override;
+
+ private:
+  void thread_run();
+
+ protected:
+  std::mutex mutex_;
+  std::condition_variable cond_;
+  bool running_ = true;
+  TimerMap timerMap_;
+  std::thread thread_;
+};
+
+Timekeeper* getTimekeeperSingleton();
+
+}} // folly::detail
diff --git a/faux-folly/folly/futures/detail/TimerMap.cpp b/faux-folly/folly/futures/detail/TimerMap.cpp
new file mode 100644
index 0000000..4a49d55
--- /dev/null
+++ b/faux-folly/folly/futures/detail/TimerMap.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 Nest Labs, Inc. All Rights Reserved.
+ *
+ * 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/futures/detail/TimerMap.h>
+#include <iterator>
+#include <vector>
+
+using std::make_tuple;
+using std::forward_as_tuple;
+
+namespace folly { namespace detail {
+
+/**
+ * Constructs a TimerMap with a predefined tick
+ *
+ * Creates an empty TimerMap with a specific tick. The tick is the
+ * minimum amount of time between timestamps returned by
+ * nextTimeout(). The intention is that the caller will only call
+ * invokeTimers() at these times.
+ */
+TimerMap::TimerMap(TimerMap::duration_type tick) :
+    tick_(tick),
+    next_tick_(TimerMap::clock_type::now()),
+    db_()
+{
+}
+
+/**
+ * Add a callback that should fire after a delay
+ *
+ * @param timeout The amount of time to delay before executing the
+ * callback.
+ *
+ * @param cb The callback/closure to execute.
+ *
+ * Note: this is a convenience function for
+ * scheduleAlarm(clock_type::now() + timeout, cb)
+ */
+void TimerMap::scheduleTimeout(TimerMap::duration_type timeout,
+                               TimerMap::callback_type cb)
+{
+    auto t0 = clock_type::now();
+    auto t1 = t0 + timeout;
+    scheduleAlarm(t1, std::move(cb));
+}
+
+/**
+ * Add a callback that should fire at a specific time
+ *
+ * @param timeout The exact time when the callback should fire.
+ *
+ * @param cb The callback/closure to execute
+ */
+void TimerMap::scheduleAlarm(TimerMap::time_point_type timeout,
+                             TimerMap::callback_type cb)
+{
+    db_.emplace(std::piecewise_construct_t(),
+                make_tuple(timeout),
+                forward_as_tuple(std::move(cb)));
+}
+
+/**
+ * Return the absolute time point when the next batch of callbacks should fire
+ *
+ * In the interest of efficiency, it's usually better to group the
+ * callback by 'tick'. Most timers do not need extremely high
+ * precision. Therefore, the nextTimeout() returns the timepoint when
+ * the next 'tick' expires (and thus you should call invokeTimers()).
+ *
+ * @return The absolute time when the invokeTimers() should next be
+ * called. The time returned may be in the past (meaning you should
+ * call it ASAP).
+ */
+TimerMap::time_point_type TimerMap::nextTimeout() const
+{
+    return next_tick_;
+}
+
+/**
+ * Execute all callbacks that have expired and remove them.
+ *
+ * This is a convenience function for SINGLE-THREADED applications
+ * (like unit tests). Do not use this if you are protecting TimerMap
+ * with any kind of lock.
+ *
+ * Usually you will be using this in a multithreaded environment, and
+ * you will have a mutex that protects this TimerMap instance. You
+ * will need to hold the lock when you call this function. However,
+ * you will want that lock to be RELEASED when the callbacks execute.
+ * This function gives you no way to do that. Instead, you should call
+ * popExpiredTimers() and invokeTimers(beg, end).
+ */
+void TimerMap::invokeTimers()
+{
+    std::vector<data_type> vec;
+
+    popExpiredTimers(std::inserter(vec, vec.begin()));
+    invokeTimers(vec.begin(), vec.end());
+}
+
+} // namespace detail
+} // namespace folly
diff --git a/faux-folly/folly/futures/detail/TimerMap.h b/faux-folly/folly/futures/detail/TimerMap.h
new file mode 100644
index 0000000..49a0e06
--- /dev/null
+++ b/faux-folly/folly/futures/detail/TimerMap.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2015 Nest Labs, Inc. All Rights Reserved.
+ *
+ * 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 <map>
+#include <chrono>
+#include <algorithm>
+
+namespace folly { namespace detail {
+
+/* A replacement for folly::HHWheelTimer (folly/io/async library) that
+ * provides O(logN) insertion. This is not a drop-in replacement for
+ * HHWHeelTimer, but is something that
+ * folly::detail::ThreadWheelTimekeeper can use for its
+ * implementation. Used to break the dependency on
+ * folly/io/async. Their hashed and heirarchical timing wheel is
+ * probably superior... and we should probably write our own.  See:
+ * http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf
+ */
+class TimerMap
+{
+public:
+    using clock_type = std::chrono::steady_clock;
+    using time_point_type = clock_type::time_point;
+    using duration_type = clock_type::duration;
+    using callback_type = std::function<void()>;
+    using multimap_type = std::multimap<time_point_type, callback_type>;
+    using data_type = std::pair<time_point_type, callback_type>;
+
+    TimerMap(duration_type tick);
+
+    /* Non-copyable */
+    TimerMap(const TimerMap& o) = delete;
+    TimerMap& operator=(const TimerMap&o) = delete;
+    TimerMap(TimerMap&& o) = delete;
+    TimerMap& operator=(TimerMap&& o) = delete;
+
+    /**
+     * Get the "tick" size for the map.
+     *
+     * @return when calling nextTimeout(), the return values will be
+     * at least this much time apart.
+     */
+    duration_type getTick() const {
+        return tick_;
+    }
+
+    /**
+     * Returns true if there are no events stored in database
+     */
+    bool empty() const {
+        return db_.empty();
+    }
+
+    void scheduleTimeout(duration_type timeout, callback_type cb);
+    void scheduleAlarm(time_point_type at, callback_type cb);
+    time_point_type nextTimeout() const;
+    void invokeTimers();
+    template <typename Iterator>
+    static void invokeTimers(Iterator beg, Iterator end);
+    template <typename InsertIterator>
+    void popExpiredTimers(InsertIterator it);
+    template <typename InsertIterator>
+    void popExpiredTimers(InsertIterator it, time_point_type t);
+
+private:
+    duration_type tick_;            //< Minimum time between next_tick_ values
+    time_point_type next_tick_;     //< The next time invokeTimers() should be called
+    multimap_type db_;              //< Internal database of timepoints and callbacks
+};
+
+/**
+ * Invoke all timers between iterators beg and end.
+ *
+ * THIS IS A STATIC FUNCTION
+ *
+ * Given an iterator pair, invoke all the callbacks/closures between
+ * them. The closures will be moved into the function and thus deleted
+ * after each execution.
+ *
+ * The Iterator (it) type must support the following expressions:
+ *
+ *    1. ++it
+ *    2. it != end
+ *    3. std::move(it->second) // extract the closure
+ *
+ * The expectation is that the iterators are pointing to a
+ * pair<Unspecified, callback_type>.
+ *
+ * @param beg iterator pointing to the first closure to execute.
+ *
+ * @param end iterator pointing to one-past-the-last closure to
+ * execute.
+ */
+template <typename Iterator>
+void TimerMap::invokeTimers(Iterator beg, Iterator end)
+{
+    Iterator cur;
+    for (cur = beg ; cur != end ; ++cur) {
+        /* destroy each closure after executing callback */
+        callback_type tmp = std::move(cur->second);
+        tmp();
+    }
+}
+
+/**
+ * Remove expired iterators from TimerMap, putting them in the provided iterator.
+ *
+ * This is a convenience function for popExpiredTimers(it, clock_type::now()).
+ * See documentation for that function.
+ *
+ * @param it insertion iterator to place events.
+ */
+template <typename InsertIterator>
+void TimerMap::popExpiredTimers(InsertIterator it) {
+    auto t = clock_type::now();
+    popExpiredTimers(it, t);
+}
+
+/**
+ * Remove expired iterators from TimerMap, putting them in the provided iterator.
+ *
+ * Since it's not possible to query how many elements will be inserted
+ * <em>a priori</em>, it is expected that you will provide an
+ * <em>insertion iterator</em>, like.
+ * std::insert_iterator<std::pair<time_point_type,
+ * callback_type>>. The iterator (it) must support the following
+ * expressions:
+ *
+ *    1. ++it
+ *    2. (*it) = std::pair<time_point_type, callback_type>(...);
+ *
+ * @param it iterator pointing to the first place where the values
+ * should be written.
+ *
+ * @param t extract all timers/callbacks/closures that expired before
+ * or at this absolute time.
+ */
+
+template <typename ForwardInputIterator>
+void TimerMap::popExpiredTimers(ForwardInputIterator it, time_point_type t) {
+    multimap_type::iterator beg = db_.begin();
+    multimap_type::iterator end = db_.upper_bound(t);
+
+    // std::move() doesn't work because multimap_type::value_type
+    // uses 'const Key' in the pair.
+    for (auto cur = beg ; cur != end ; ++cur, ++it) {
+        (*it) = std::make_pair(cur->first, std::move(cur->second));
+    }
+
+    db_.erase(beg, end);
+    next_tick_ = t + tick_;
+}
+
+} // namespace detail
+} // namespace folly
diff --git a/faux-folly/folly/futures/detail/Types.h b/faux-folly/folly/futures/detail/Types.h
new file mode 100644
index 0000000..6f87b2f
--- /dev/null
+++ b/faux-folly/folly/futures/detail/Types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 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 <chrono>
+
+namespace folly {
+
+/// folly::Duration is an alias for the best resolution we offer/work with.
+/// However, it is not intended to be used for client code - you should use a
+/// descriptive std::chrono::duration type instead. e.g. do not write this:
+///
+///   futures::sleep(Duration(1000))...
+///
+/// rather this:
+///
+///   futures::sleep(std::chrono::milliseconds(1000));
+///
+/// or this:
+///
+///   futures::sleep(std::chrono::seconds(1));
+using Duration = std::chrono::milliseconds;
+
+}
diff --git a/faux-folly/folly/futures/exercises/01-Values.cpp b/faux-folly/folly/futures/exercises/01-Values.cpp
new file mode 100644
index 0000000..04ba095
--- /dev/null
+++ b/faux-folly/folly/futures/exercises/01-Values.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 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 "Koan.h"
+#include <folly/futures/Future.h>
+
+using folly::Future;
+using folly::makeFuture;
+
+#if 0 // compilation cursor
+
+TEST(Values, canonicalForm) {
+  // The canonical way to make a Future from an immediate value is with the
+  // Future constructor.
+  Future<int> answer(__);
+  EXPECT_EQ(42, answer.get());
+}
+
+TEST(Values, typeDeduction) {
+  // If you use makeFuture, the compiler will deduce the type.
+  auto answer = makeFuture(__);
+  EXPECT_EQ(42, answer.get());
+}
+
+TEST(Values, exceptionNeedsType) {
+  // To create a Future holding an exception, you must
+  // use makeFuture with the type
+  std::runtime_error err("Don't Panic");
+  auto question = __(err);
+  // not
+  //auto question = makeFuture(err);
+  EXPECT_THROW(question.get(), std::runtime_error);
+}
+
+TEST(Values, typeConversion) {
+  // Sometimes it's cleaner to give the type and let the compiler do implicit
+  // type conversion
+  __ answer(42);
+  // not
+  //auto answer = makeFuture((double)42);
+  EXPECT_EQ(__, answer.get());
+}
+
+using folly::Try;
+
+TEST(Values, tryInside) {
+  // Futures hold either a Value or Exception. This is accomplished under the
+  // covers with Try
+  Try<int> t = makeFuture(42).__();
+  EXPECT_TRUE(t.hasValue());
+  EXPECT_EQ(42, t.value());
+
+  t = Future<int>(std::runtime_error("Don't Panic")).__();
+  EXPECT_TRUE(t.hasException());
+  EXPECT_THROW(t.value(), std::runtime_error);
+}
+
+#endif
diff --git a/faux-folly/folly/futures/exercises/Koan.h b/faux-folly/folly/futures/exercises/Koan.h
new file mode 100644
index 0000000..7b822f8
--- /dev/null
+++ b/faux-folly/folly/futures/exercises/Koan.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+#include <gflags/gflags.h>
diff --git a/faux-folly/folly/futures/exercises/README.md b/faux-folly/folly/futures/exercises/README.md
new file mode 100644
index 0000000..e5aecf8
--- /dev/null
+++ b/faux-folly/folly/futures/exercises/README.md
@@ -0,0 +1,9 @@
+Working through [Programming Koans][1] can be a fun, lightweight, and fast way to
+learn a new language or pattern with small, focused exercises. Therefore, we
+present Koans for Futures.  Perhaps you will call them "Koans to Be".
+
+Edit the topical files one at a time, moving the "compilation cursor" down one
+exercise at a time, and getting them to pass by filling in the blanks.
+
+[1] http://www.lauradhamilton.com/learn-a-new-programming-language-today-with-koans
+[2] http://catb.org/esr/writings/unix-koans/ten-thousand.html
diff --git a/faux-folly/folly/futures/exercises/main.cpp b/faux-folly/folly/futures/exercises/main.cpp
new file mode 100644
index 0000000..ab10452
--- /dev/null
+++ b/faux-folly/folly/futures/exercises/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 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 "Koan.h"
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/futures/helpers.h b/faux-folly/folly/futures/helpers.h
new file mode 100644
index 0000000..30e657c
--- /dev/null
+++ b/faux-folly/folly/futures/helpers.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2015 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/futures/Future.h>
+#include <folly/Portability.h>
+
+namespace folly {
+
+/// This namespace is for utility functions that would usually be static
+/// members of Future, except they don't make sense there because they don't
+/// depend on the template type (rather, on the type of their arguments in
+/// some cases). This is the least-bad naming scheme we could think of. Some
+/// of the functions herein have really-likely-to-collide names, like "map"
+/// and "sleep".
+namespace futures {
+  /// Returns a Future that will complete after the specified duration. The
+  /// Duration typedef of a `std::chrono` duration type indicates the
+  /// resolution you can expect to be meaningful (milliseconds at the time of
+  /// writing). Normally you wouldn't need to specify a Timekeeper, we will
+  /// use the global futures timekeeper (we run a thread whose job it is to
+  /// keep time for futures timeouts) but we provide the option for power
+  /// users.
+  ///
+  /// The Timekeeper thread will be lazily created the first time it is
+  /// needed. If your program never uses any timeouts or other time-based
+  /// Futures you will pay no Timekeeper thread overhead.
+  Future<Unit> sleep(Duration, Timekeeper* = nullptr);
+
+  /**
+   * Set func as the callback for each input Future and return a vector of
+   * Futures containing the results in the input order.
+   */
+  template <class It, class F,
+            class ItT = typename std::iterator_traits<It>::value_type,
+            class Result
+      = typename decltype(std::declval<ItT>().then(std::declval<F>()))::value_type>
+  std::vector<Future<Result>> map(It first, It last, F func);
+
+  // Sugar for the most common case
+  template <class Collection, class F>
+  auto map(Collection&& c, F&& func)
+      -> decltype(map(c.begin(), c.end(), func)) {
+    return map(c.begin(), c.end(), std::forward<F>(func));
+  }
+
+} // namespace futures
+
+/**
+  Make a completed Future by moving in a value. e.g.
+
+    string foo = "foo";
+    auto f = makeFuture(std::move(foo));
+
+  or
+
+    auto f = makeFuture<string>("foo");
+*/
+template <class T>
+Future<typename std::decay<T>::type> makeFuture(T&& t);
+
+/** Make a completed void Future. */
+Future<Unit> makeFuture();
+
+/** Make a completed Future by executing a function. If the function throws
+  we capture the exception, otherwise we capture the result. */
+template <class F>
+auto makeFutureWith(F&& func)
+    -> Future<typename Unit::Lift<decltype(func())>::type>;
+
+/// Make a failed Future from an exception_ptr.
+/// Because the Future's type cannot be inferred you have to specify it, e.g.
+///
+///   auto f = makeFuture<string>(std::current_exception());
+template <class T>
+FOLLY_DEPRECATED("use makeFuture(exception_wrapper)")
+Future<T> makeFuture(std::exception_ptr const& e);
+
+/// Make a failed Future from an exception_wrapper.
+template <class T>
+Future<T> makeFuture(exception_wrapper ew);
+
+/** Make a Future from an exception type E that can be passed to
+  std::make_exception_ptr(). */
+template <class T, class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value,
+                        Future<T>>::type
+makeFuture(E const& e);
+
+/** Make a Future out of a Try */
+template <class T>
+Future<T> makeFuture(Try<T>&& t);
+
+/*
+ * Return a new Future that will call back on the given Executor.
+ * This is just syntactic sugar for makeFuture().via(executor)
+ *
+ * @param executor the Executor to call back on
+ * @param priority optionally, the priority to add with. Defaults to 0 which
+ * represents medium priority.
+ *
+ * @returns a void Future that will call back on the given executor
+ */
+inline Future<Unit> via(
+    Executor* executor,
+    int8_t priority = Executor::MID_PRI);
+
+/// Execute a function via the given executor and return a future.
+/// This is semantically equivalent to via(executor).then(func), but
+/// easier to read and slightly more efficient.
+template <class Func>
+auto via(Executor*, Func func)
+  -> Future<typename isFuture<decltype(func())>::Inner>;
+
+/** When all the input Futures complete, the returned Future will complete.
+  Errors do not cause early termination; this Future will always succeed
+  after all its Futures have finished (whether successfully or with an
+  error).
+
+  The Futures are moved in, so your copies are invalid. If you need to
+  chain further from these Futures, use the variant with an output iterator.
+
+  This function is thread-safe for Futures running on different threads. But
+  if you are doing anything non-trivial after, you will probably want to
+  follow with `via(executor)` because it will complete in whichever thread the
+  last Future completes in.
+
+  The return type for Future<T> input is a Future<std::vector<Try<T>>>
+  */
+template <class InputIterator>
+Future<std::vector<Try<
+  typename std::iterator_traits<InputIterator>::value_type::value_type>>>
+collectAll(InputIterator first, InputIterator last);
+
+/// Sugar for the most common case
+template <class Collection>
+auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {
+  return collectAll(c.begin(), c.end());
+}
+
+/// This version takes a varying number of Futures instead of an iterator.
+/// The return type for (Future<T1>, Future<T2>, ...) input
+/// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>.
+/// The Futures are moved in, so your copies are invalid.
+template <typename... Fs>
+typename detail::CollectAllVariadicContext<
+  typename std::decay<Fs>::type::value_type...>::type
+collectAll(Fs&&... fs);
+
+/// Like collectAll, but will short circuit on the first exception. Thus, the
+/// type of the returned Future is std::vector<T> instead of
+/// std::vector<Try<T>>
+template <class InputIterator>
+Future<typename detail::CollectContext<
+  typename std::iterator_traits<InputIterator>::value_type::value_type
+>::result_type>
+collect(InputIterator first, InputIterator last);
+
+/// Sugar for the most common case
+template <class Collection>
+auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {
+  return collect(c.begin(), c.end());
+}
+
+/// Like collectAll, but will short circuit on the first exception. Thus, the
+/// type of the returned Future is std::tuple<T1, T2, ...> instead of
+/// std::tuple<Try<T1>, Try<T2>, ...>
+template <typename... Fs>
+typename detail::CollectVariadicContext<
+  typename std::decay<Fs>::type::value_type...>::type
+collect(Fs&&... fs);
+
+/** The result is a pair of the index of the first Future to complete and
+  the Try. If multiple Futures complete at the same time (or are already
+  complete when passed in), the "winner" is chosen non-deterministically.
+
+  This function is thread-safe for Futures running on different threads.
+  */
+template <class InputIterator>
+Future<std::pair<
+  size_t,
+  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
+collectAny(InputIterator first, InputIterator last);
+
+/// Sugar for the most common case
+template <class Collection>
+auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) {
+  return collectAny(c.begin(), c.end());
+}
+
+/** when n Futures have completed, the Future completes with a vector of
+  the index and Try of those n Futures (the indices refer to the original
+  order, but the result vector will be in an arbitrary order)
+
+  Not thread safe.
+  */
+template <class InputIterator>
+Future<std::vector<std::pair<
+  size_t,
+  Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
+collectN(InputIterator first, InputIterator last, size_t n);
+
+/// Sugar for the most common case
+template <class Collection>
+auto collectN(Collection&& c, size_t n)
+    -> decltype(collectN(c.begin(), c.end(), n)) {
+  return collectN(c.begin(), c.end(), n);
+}
+
+/** window creates up to n Futures using the values
+    in the collection, and then another Future for each Future
+    that completes
+
+    this is basically a sliding window of Futures of size n
+
+    func must return a Future for each value in input
+  */
+template <class Collection, class F,
+          class ItT = typename std::iterator_traits<
+            typename Collection::iterator>::value_type,
+          class Result = typename detail::resultOf<F, ItT&&>::value_type>
+std::vector<Future<Result>>
+window(Collection input, F func, size_t n);
+
+template <typename F, typename T, typename ItT>
+using MaybeTryArg = typename std::conditional<
+  detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type;
+
+template<typename F, typename T, typename Arg>
+using isFutureResult = isFuture<typename std::result_of<F(T&&, Arg&&)>::type>;
+
+/** repeatedly calls func on every result, e.g.
+    reduce(reduce(reduce(T initial, result of first), result of second), ...)
+
+    The type of the final result is a Future of the type of the initial value.
+
+    Func can either return a T, or a Future<T>
+
+    func is called in order of the input, see unorderedReduce if that is not
+    a requirement
+  */
+template <class It, class T, class F>
+Future<T> reduce(It first, It last, T&& initial, F&& func);
+
+/// Sugar for the most common case
+template <class Collection, class T, class F>
+auto reduce(Collection&& c, T&& initial, F&& func)
+    -> decltype(reduce(c.begin(), c.end(), std::forward<T>(initial),
+                std::forward<F>(func))) {
+  return reduce(
+      c.begin(),
+      c.end(),
+      std::forward<T>(initial),
+      std::forward<F>(func));
+}
+
+/** like reduce, but calls func on finished futures as they complete
+    does NOT keep the order of the input
+  */
+template <class It, class T, class F,
+          class ItT = typename std::iterator_traits<It>::value_type::value_type,
+          class Arg = MaybeTryArg<F, T, ItT>>
+Future<T> unorderedReduce(It first, It last, T initial, F func);
+
+/// Sugar for the most common case
+template <class Collection, class T, class F>
+auto unorderedReduce(Collection&& c, T&& initial, F&& func)
+    -> decltype(unorderedReduce(c.begin(), c.end(), std::forward<T>(initial),
+                std::forward<F>(func))) {
+  return unorderedReduce(
+      c.begin(),
+      c.end(),
+      std::forward<T>(initial),
+      std::forward<F>(func));
+}
+
+namespace futures {
+
+/**
+ *  retrying
+ *
+ *  Given a policy and a future-factory, creates futures according to the
+ *  policy.
+ *
+ *  The policy must be moveable - retrying will move it a lot - and callable of
+ *  either of the two forms:
+ *  - Future<bool>(size_t, exception_wrapper)
+ *  - bool(size_t, exception_wrapper)
+ *  Internally, the latter is transformed into the former in the obvious way.
+ *  The first parameter is the attempt number of the next prospective attempt;
+ *  the second parameter is the most recent exception. The policy returns a
+ *  Future<bool> which, when completed with true, indicates that a retry is
+ *  desired.
+ *
+ *  We provide a few generic policies:
+ *  - Basic
+ *  - CappedJitteredexponentialBackoff
+ *
+ *  Custom policies may use the most recent try number and exception to decide
+ *  whether to retry and optionally to do something interesting like delay
+ *  before the retry. Users may pass inline lambda expressions as policies, or
+ *  may define their own data types meeting the above requirements. Users are
+ *  responsible for managing the lifetimes of anything pointed to or referred to
+ *  from inside the policy.
+ *
+ *  For example, one custom policy may try up to k times, but only if the most
+ *  recent exception is one of a few types or has one of a few error codes
+ *  indicating that the failure was transitory.
+ *
+ *  Cancellation is not supported.
+ */
+template <class Policy, class FF>
+typename std::result_of<FF(size_t)>::type
+retrying(Policy&& p, FF&& ff);
+
+/**
+ *  generic retrying policies
+ */
+
+inline
+std::function<bool(size_t, const exception_wrapper&)>
+retryingPolicyBasic(
+    size_t max_tries);
+
+template <class Policy, class URNG>
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param,
+    URNG rng,
+    Policy&& p);
+
+inline
+std::function<Future<bool>(size_t, const exception_wrapper&)>
+retryingPolicyCappedJitteredExponentialBackoff(
+    size_t max_tries,
+    Duration backoff_min,
+    Duration backoff_max,
+    double jitter_param);
+
+}
+
+} // namespace
diff --git a/faux-folly/folly/futures/test/BarrierTest.cpp b/faux-folly/folly/futures/test/BarrierTest.cpp
new file mode 100644
index 0000000..52faf8e
--- /dev/null
+++ b/faux-folly/folly/futures/test/BarrierTest.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 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/futures/Barrier.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+#include <folly/Random.h>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+DEFINE_int32(seed, 0, "Random seed");
+
+namespace folly { namespace futures { namespace test {
+
+TEST(BarrierTest, Simple) {
+  constexpr uint32_t numThreads = 10;
+
+  std::mutex mutex;
+  std::condition_variable b1DoneCond;
+  std::condition_variable b2DoneCond;
+  std::atomic<uint32_t> b1TrueSeen(0);
+  std::atomic<uint32_t> b1Passed(0);
+  std::atomic<uint32_t> b2TrueSeen(0);
+  std::atomic<uint32_t> b2Passed(0);
+
+  Barrier barrier(numThreads + 1);
+
+  std::vector<std::thread> threads;
+  threads.reserve(numThreads);
+  for (uint32_t i = 0; i < numThreads; ++i) {
+    threads.emplace_back([&] () {
+      barrier.wait()
+        .then(
+            [&] (bool v) {
+              std::unique_lock<std::mutex> lock(mutex);
+              b1TrueSeen += uint32_t(v);
+              if (++b1Passed == numThreads) {
+                b1DoneCond.notify_one();
+              }
+              return barrier.wait();
+            })
+        .then(
+            [&] (bool v) {
+              std::unique_lock<std::mutex> lock(mutex);
+              b2TrueSeen += uint32_t(v);
+              if (++b2Passed == numThreads) {
+                b2DoneCond.notify_one();
+              }
+            })
+        .get();
+    });
+  }
+
+  /* sleep override */
+  std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  EXPECT_EQ(0, b1Passed);
+  EXPECT_EQ(0, b1TrueSeen);
+
+  b1TrueSeen += barrier.wait().get();
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    while (b1Passed != numThreads) {
+      b1DoneCond.wait(lock);
+    }
+    EXPECT_EQ(1, b1TrueSeen);
+  }
+
+  /* sleep override */
+  std::this_thread::sleep_for(std::chrono::milliseconds(50));
+  EXPECT_EQ(0, b2Passed);
+  EXPECT_EQ(0, b2TrueSeen);
+
+  b2TrueSeen += barrier.wait().get();
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    while (b2Passed != numThreads) {
+      b2DoneCond.wait(lock);
+    }
+    EXPECT_EQ(1, b2TrueSeen);
+  }
+
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+TEST(BarrierTest, Random) {
+  // Create numThreads threads.
+  //
+  // Each thread repeats the following numIterations times:
+  //   - grab a randomly chosen number of futures from the barrier, waiting
+  //     for a short random time between each
+  //   - wait for all futures to complete
+  //   - record whether the one future returning true was seen among them
+  //
+  // At the end, we verify that exactly one future returning true was seen
+  // for each iteration.
+  constexpr uint32_t numIterations = 1;
+  auto numThreads = folly::Random::rand32(30, 91);
+
+  struct ThreadInfo {
+    ThreadInfo() { }
+    std::thread thread;
+    uint32_t iteration = 0;
+    uint32_t numFutures;
+    std::vector<uint32_t> trueSeen;
+  };
+
+  std::vector<ThreadInfo> threads;
+  threads.resize(numThreads);
+
+  uint32_t totalFutures = 0;
+  for (auto& tinfo : threads) {
+    tinfo.numFutures = folly::Random::rand32(100);
+    tinfo.trueSeen.resize(numIterations);
+    totalFutures += tinfo.numFutures;
+  }
+
+  Barrier barrier(totalFutures);
+
+  for (auto& tinfo : threads) {
+    auto pinfo = &tinfo;
+    tinfo.thread = std::thread(
+        [numIterations, pinfo, &barrier] () {
+          std::vector<folly::Future<bool>> futures;
+          futures.reserve(pinfo->numFutures);
+          for (uint32_t i = 0; i < numIterations; ++i, ++pinfo->iteration) {
+            futures.clear();
+            for (uint32_t j = 0; j < pinfo->numFutures; ++j) {
+              futures.push_back(barrier.wait());
+              auto nanos = folly::Random::rand32(10 * 1000 * 1000);
+              /* sleep override */
+              std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
+            }
+            auto results = folly::collect(futures).get();
+            pinfo->trueSeen[i] =
+              std::count(results.begin(), results.end(), true);
+          }
+        });
+  }
+
+  for (auto& tinfo : threads) {
+    tinfo.thread.join();
+    EXPECT_EQ(numIterations, tinfo.iteration);
+  }
+
+  for (uint32_t i = 0; i < numIterations; ++i) {
+    uint32_t trueCount = 0;
+    for (auto& tinfo : threads) {
+      trueCount += tinfo.trueSeen[i];
+    }
+    EXPECT_EQ(1, trueCount);
+  }
+}
+
+}}}  // namespaces
diff --git a/faux-folly/folly/futures/test/Benchmark.cpp b/faux-folly/folly/futures/test/Benchmark.cpp
new file mode 100644
index 0000000..6151832
--- /dev/null
+++ b/faux-folly/folly/futures/test/Benchmark.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2015 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 <gflags/gflags.h>
+
+#include <folly/Baton.h>
+#include <folly/Benchmark.h>
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+#include <folly/futures/InlineExecutor.h>
+
+#include <semaphore.h>
+#include <vector>
+
+using namespace folly;
+
+namespace {
+
+template <class T>
+T incr(Try<T>&& t) {
+  return t.value() + 1;
+}
+
+void someThens(size_t n) {
+  auto f = makeFuture<int>(42);
+  for (size_t i = 0; i < n; i++) {
+    f = f.then(incr<int>);
+  }
+}
+
+} // anonymous namespace
+
+BENCHMARK(constantFuture) {
+  makeFuture(42);
+}
+
+BENCHMARK_RELATIVE(promiseAndFuture) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  p.setValue(42);
+  f.value();
+}
+
+BENCHMARK_RELATIVE(withThen) {
+  Promise<int> p;
+  Future<int> f = p.getFuture().then(incr<int>);
+  p.setValue(42);
+  f.value();
+}
+
+// thens
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(oneThen) {
+  someThens(1);
+}
+
+// look for >= 50% relative
+BENCHMARK_RELATIVE(twoThens) {
+  someThens(2);
+}
+
+// look for >= 25% relative
+BENCHMARK_RELATIVE(fourThens) {
+  someThens(4);
+}
+
+// look for >= 1% relative
+BENCHMARK_RELATIVE(hundredThens) {
+  someThens(100);
+}
+
+// Lock contention. Although in practice fulfills tend to be temporally
+// separate from then()s, still sometimes they will be concurrent. So the
+// higher this number is, the better.
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(no_contention) {
+  std::vector<Promise<int>> promises(10000);
+  std::vector<Future<int>> futures;
+  std::thread producer, consumer;
+
+  BENCHMARK_SUSPEND {
+    folly::Baton<> b1, b2;
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    consumer = std::thread([&]{
+      b1.post();
+      for (auto& f : futures) f.then(incr<int>);
+    });
+    consumer.join();
+
+    producer = std::thread([&]{
+      b2.post();
+      for (auto& p : promises) p.setValue(42);
+    });
+
+    b1.wait();
+    b2.wait();
+  }
+
+  // The only thing we are measuring is how long fulfill + callbacks take
+  producer.join();
+}
+
+BENCHMARK_RELATIVE(contention) {
+  std::vector<Promise<int>> promises(10000);
+  std::vector<Future<int>> futures;
+  std::thread producer, consumer;
+  sem_t sem;
+  sem_init(&sem, 0, 0);
+
+  BENCHMARK_SUSPEND {
+    folly::Baton<> b1, b2;
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    consumer = std::thread([&]{
+      b1.post();
+      for (auto& f : futures) {
+        sem_wait(&sem);
+        f.then(incr<int>);
+      }
+    });
+
+    producer = std::thread([&]{
+      b2.post();
+      for (auto& p : promises) {
+        sem_post(&sem);
+        p.setValue(42);
+      }
+    });
+
+    b1.wait();
+    b2.wait();
+  }
+
+  // The astute reader will notice that we're not *precisely* comparing apples
+  // to apples here. Well, maybe it's like comparing Granny Smith to
+  // Braeburn or something. In the serial version, we waited for the futures
+  // to be all set up, but here we are probably still doing that work
+  // (although in parallel). But even though there is more work (on the order
+  // of 2x), it is being done by two threads. Hopefully most of the difference
+  // we see is due to lock contention and not false parallelism.
+  //
+  // Be warned that if the box is under heavy load, this will greatly skew
+  // these results (scheduling overhead will begin to dwarf lock contention).
+  // I'm not sure but I'd guess in Windtunnel this will mean large variance,
+  // because I expect they load the boxes as much as they can?
+  consumer.join();
+  producer.join();
+}
+
+BENCHMARK_DRAW_LINE();
+
+// The old way. Throw an exception, and rethrow to access it upstream.
+void throwAndCatchImpl() {
+  makeFuture()
+      .then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
+      .then([](Try<Unit>&& t) {
+        try {
+          t.value();
+        } catch(const std::runtime_error& e) {
+          // ...
+          return;
+        }
+        CHECK(false);
+      });
+}
+
+// Not much better. Throw an exception, and access it via the wrapper upstream.
+// Actually a little worse due to wrapper overhead. then() won't know that the
+// exception is a runtime_error, so will have to store it as an exception_ptr
+// anyways. withException will therefore have to rethrow. Note that if we threw
+// std::exception instead, we would see some wins, as that's the type then()
+// will try to wrap, so no exception_ptrs/rethrows are necessary.
+void throwAndCatchWrappedImpl() {
+  makeFuture()
+      .then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
+      .then([](Try<Unit>&& t) {
+        auto caught = t.withException<std::runtime_error>(
+            [](const std::runtime_error& e){
+              // ...
+            });
+        CHECK(caught);
+      });
+}
+
+// Better. Wrap an exception, and rethrow to access it upstream.
+void throwWrappedAndCatchImpl() {
+  makeFuture()
+      .then([](Try<Unit>&&){
+        return makeFuture<Unit>(std::runtime_error("oh no"));
+      })
+      .then([](Try<Unit>&& t) {
+        try {
+          t.value();
+        } catch(const std::runtime_error& e) {
+          // ...
+          return;
+        }
+        CHECK(false);
+      });
+}
+
+// The new way. Wrap an exception, and access it via the wrapper upstream
+void throwWrappedAndCatchWrappedImpl() {
+  makeFuture()
+      .then([](Try<Unit>&&){
+        return makeFuture<Unit>(std::runtime_error("oh no"));
+      })
+      .then([](Try<Unit>&& t){
+        auto caught = t.withException<std::runtime_error>(
+            [](const std::runtime_error& e){
+              // ...
+            });
+        CHECK(caught);
+      });
+}
+
+// Simulate heavy contention on func
+void contend(void(*func)()) {
+  folly::BenchmarkSuspender s;
+  const int N = 100;
+  const int iters = 1000;
+  pthread_barrier_t barrier;
+  pthread_barrier_init(&barrier, nullptr, N+1);
+  std::vector<std::thread> threads;
+  for (int i = 0; i < N; i++) {
+    threads.push_back(std::thread([&](){
+      pthread_barrier_wait(&barrier);
+      for (int j = 0; j < iters; j++) {
+        func();
+      }
+    }));
+  }
+  pthread_barrier_wait(&barrier);
+  s.dismiss();
+  for (auto& t : threads) {
+    t.join();
+  }
+  s.rehire();
+  pthread_barrier_destroy(&barrier);
+}
+
+BENCHMARK(throwAndCatch) {
+  throwAndCatchImpl();
+}
+
+BENCHMARK_RELATIVE(throwAndCatchWrapped) {
+  throwAndCatchWrappedImpl();
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatch) {
+  throwWrappedAndCatchImpl();
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {
+  throwWrappedAndCatchWrappedImpl();
+}
+
+BENCHMARK_DRAW_LINE();
+
+BENCHMARK(throwAndCatchContended) {
+  contend(throwAndCatchImpl);
+}
+
+BENCHMARK_RELATIVE(throwAndCatchWrappedContended) {
+  contend(throwAndCatchWrappedImpl);
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatchContended) {
+  contend(throwWrappedAndCatchImpl);
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {
+  contend(throwWrappedAndCatchWrappedImpl);
+}
+
+InlineExecutor exe;
+
+template <class T>
+Future<T> fGen() {
+  Promise<T> p;
+  auto f = p.getFuture()
+    .then([] (T&& t) {
+      return std::move(t);
+    })
+    .then([] (T&& t) {
+      return makeFuture(std::move(t));
+    })
+    .via(&exe)
+    .then([] (T&& t) {
+      return std::move(t);
+    })
+    .then([] (T&& t) {
+      return makeFuture(std::move(t));
+    });
+  p.setValue(T());
+  return f;
+}
+
+template <class T>
+std::vector<Future<T>> fsGen() {
+  std::vector<Future<T>> fs;
+  for (auto i = 0; i < 10; i++) {
+    fs.push_back(fGen<T>());
+  }
+  return fs;
+}
+
+template <class T>
+void complexBenchmark() {
+  collect(fsGen<T>());
+  collectAll(fsGen<T>());
+  collectAny(fsGen<T>());
+  futures::map(fsGen<T>(), [] (const T& t) {
+    return t;
+  });
+  futures::map(fsGen<T>(), [] (const T& t) {
+    return makeFuture(T(t));
+  });
+}
+
+BENCHMARK_DRAW_LINE();
+
+template <size_t S>
+struct Blob {
+  char buf[S];
+};
+
+BENCHMARK(complexUnit) {
+  complexBenchmark<Unit>();
+}
+
+BENCHMARK_RELATIVE(complexBlob4) {
+  complexBenchmark<Blob<4>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob8) {
+  complexBenchmark<Blob<8>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob64) {
+  complexBenchmark<Blob<64>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob128) {
+  complexBenchmark<Blob<128>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob256) {
+  complexBenchmark<Blob<256>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob512) {
+  complexBenchmark<Blob<512>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob1024) {
+  complexBenchmark<Blob<1024>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob2048) {
+  complexBenchmark<Blob<2048>>();
+}
+
+BENCHMARK_RELATIVE(complexBlob4096) {
+  complexBenchmark<Blob<4096>>();
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
diff --git a/faux-folly/folly/futures/test/CancellationTest.cpp b/faux-folly/folly/futures/test/CancellationTest.cpp
new file mode 100644
index 0000000..a7e7120
--- /dev/null
+++ b/faux-folly/folly/futures/test/CancellationTest.cpp
@@ -0,0 +1,908 @@
+/*
+ * Copyright 2015 Nest Labs, 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 <gtest/gtest.h>
+
+#include <string>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+#include <folly/futures/InlineExecutor.h>
+#include <folly/futures/ManualExecutor.h>
+
+using namespace folly;
+
+/* If a Cancellation is invoked, the continuation shall not be invoked */
+TEST(CancellationFutures, basicOperation) {
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        InlineExecutor x;
+        Cancellation cx0, cx1;
+
+        auto f0 = p0.getFuture()
+            .via(&x, cx0)
+            .then([&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .via(&x, cx1)
+            .then([&k1] (unsigned v) { k1 = v; });
+
+        cx0.cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(9, k1); /* not cancelled */
+}
+
+/* If a Cancellation is invoked, the continuation shall not be invoked
+ * The Cancellation is provided via .then call. Using .then makes the code simpler and clearer:
+ *      instead of:
+ *          f.via(x, cx).then(a);
+ *      use:
+ *          f.then(x, cx, a);
+ */
+TEST(CancellationFutures, Then_basicOperation) {
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        InlineExecutor x;
+        Cancellation cx0, cx1;
+
+        auto f0 = p0.getFuture()
+            .then(&x, cx0, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .then(&x, cx1, [&k1] (unsigned v) { k1 = v; });
+
+        cx0.cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(9, k1); /* not cancelled */
+}
+
+/* If a Cancellation is invoked, the continuation shall not be invoked
+ * The Cancellation is provided via .thenMultiWithExecutor call.
+ * Using .thenMultiWithExecutor makes the code simpler and clearer if callbacks chain is used:
+ *      instead of:
+ *          f.via(x, cx).then(a).then(b).then(c)
+ *      use:
+ *          f.thenMultiWithExecutor(x, cx, a, b, c);
+ */
+TEST(CancellationFutures, thenMulti_basicOperation) {
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        InlineExecutor x;
+        Cancellation cx0, cx1;
+
+        auto f0 = p0.getFuture()
+            .thenMultiWithExecutor(&x, cx0, [&k0] (unsigned v) { k0 = v; });
+
+        auto f1 = p1.getFuture()
+            .thenMultiWithExecutor(&x, cx1, [] (unsigned v) { return v; }, [&k1] (unsigned v) { k1 = v; });
+        cx0.cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(9, k1); /* not cancelled */
+}
+
+/* Cancellations may be shared among multiple futures */
+TEST(CancellationFutures, sharedCancellations) {
+    InlineExecutor exe;
+    Cancellation cx0, cx1;
+    Promise<unsigned> p00, p01, p02, p03, p04;
+    Promise<unsigned> p10, p11, p12, p13, p14;
+    auto lam_control = [&] { EXPECT_TRUE(true); };
+    auto lam_variable = [&] { EXPECT_TRUE(false); };
+
+    p00.getFuture().via(&exe, cx0).then(lam_control);
+    p01.getFuture().via(&exe, cx0).then(lam_control);
+    p02.getFuture().via(&exe, cx0).then(lam_control);
+    p03.getFuture().via(&exe, cx0).then(lam_control);
+    p04.getFuture().via(&exe, cx0).then(lam_control);
+
+    p10.getFuture().via(&exe, cx1).then(lam_variable);
+    p11.getFuture().via(&exe, cx1).then(lam_variable);
+    p12.getFuture().via(&exe, cx1).then(lam_variable);
+    p13.getFuture().via(&exe, cx1).then(lam_variable);
+    p14.getFuture().via(&exe, cx1).then(lam_variable);
+
+    cx1.cancel();
+
+    p00.setValue(0);
+    p01.setValue(1);
+    p02.setValue(2);
+    p03.setValue(3);
+    p04.setValue(4);
+    p10.setValue(5);
+    p11.setValue(6);
+    p12.setValue(7);
+    p13.setValue(8);
+    p14.setValue(9);
+}
+
+/* Cancellations via .then may be shared among multiple futures */
+TEST(CancellationFutures, Then_sharedCancellations) {
+    InlineExecutor exe;
+    Cancellation cx0, cx1;
+    Promise<unsigned> p00, p01;
+    Promise<unsigned> p10, p11;
+    unsigned k00 = 0, k01 = 0, k10 = 0, k11 = 0;
+
+    p00.getFuture().then(&exe, cx0, [&k00](unsigned v) { k00 = v; });
+    p01.getFuture().then(&exe, cx0, [&k01](unsigned v) { k01 = v; });
+    p10.getFuture().then(&exe, cx1, [&k10](unsigned v) { k10 = v; });
+    p11.getFuture().then(&exe, cx1, [&k11](unsigned v) { k11 = v; });
+
+    cx1.cancel();
+
+    p00.setValue(1);
+    p01.setValue(2);
+    p10.setValue(8);
+    p11.setValue(9);
+
+    EXPECT_EQ(1, k00); /* not cancelled */
+    EXPECT_EQ(2, k01); /* not cancelled */
+    EXPECT_EQ(0, k10); /* cancelled */
+    EXPECT_EQ(0, k11); /* cancelled */
+}
+
+/* Cancellations via shall protect a dangling Executor* reference */
+TEST(CancellationFutures, danglingExecutorPointer) {
+    using std::unique_ptr;
+    using std::string;
+
+    unique_ptr<ManualExecutor> x(new ManualExecutor);
+    Promise<string> p;
+    Cancellation cx;
+    string v;
+
+    p.getFuture()
+        .via(x.get(), cx)
+        .then([&] (string s) { v = s; });
+
+    cx.cancel();
+    x.reset();
+
+    p.setValue("foo"); // should not segfault
+
+    EXPECT_EQ("", v);
+}
+
+/* Cancellations via .then shall protect a dangling Executor* reference */
+TEST(CancellationFutures, Then_danglingExecutorPointer) {
+    using std::unique_ptr;
+    using std::string;
+
+    unique_ptr<ManualExecutor> x(new ManualExecutor);
+    Promise<string> p;
+    Cancellation cx;
+    string v;
+
+    p.getFuture()
+        .then(x.get(), cx, [&] (string s) { v = s; });
+
+    cx.cancel();
+    x.reset();
+
+    p.setValue("foo"); // should not segfault
+
+    EXPECT_EQ("", v);
+}
+
+/* Cancellations shall prevent continuation from executing, even if scheduled on Executor
+ *
+ * This tests a specific race condition where a Cancellation is:
+ *
+ * 1. cancelled /after/ being scheduled on the Executor,
+ * 2. protecting some other reference (e.g. Object&),
+ * 3. it is cancelled before the Object is deleted, and
+ * 4. The executor fires the callback /after/ the Object is deleted.
+ *
+ * When this happens, the future's continuation should /not/ fire.
+ */
+TEST(CancellationFutures, cancelAfterExecutorAdd)
+{
+    ManualExecutor x;
+    Promise<int> p;
+    Cancellation cx;
+    int flag0 = 0;
+    int flag1 = 0;
+    int val = 0;
+
+    p.getFuture()
+        .via(&x, cx)
+        .then([&] (int i) {
+                sleep(1);
+                val = i;
+            });
+
+    x.add([&] { flag0 = 1; });
+    p.setValue(42);
+    x.add([&] { flag1 = 1; });
+
+    cx.cancel();
+
+    size_t cbs = x.run();
+    EXPECT_EQ(3, cbs);
+    EXPECT_EQ(1, flag0);
+    EXPECT_EQ(0, val);
+    EXPECT_EQ(1, flag1);
+}
+
+/* Cancellations via .then shall prevent continuation from executing, even if scheduled on Executor
+ *
+ * This tests a specific race condition where a Cancellation is:
+ *
+ * 1. cancelled /after/ being scheduled on the Executor,
+ * 2. protecting some other reference (e.g. Object&),
+ * 3. it is cancelled before the Object is deleted, and
+ * 4. The executor fires the callback /after/ the Object is deleted.
+ *
+ * When this happens, the future's continuation should /not/ fire.
+ */
+TEST(CancellationFutures, Then_cancelAfterExecutorAdd)
+{
+    ManualExecutor x;
+    Promise<int> p;
+    Cancellation cx;
+    int flag0 = 0;
+    int flag1 = 0;
+    int val = 0;
+
+    p.getFuture()
+        .then(&x, cx, [&] (int i) {
+                val = i;
+            });
+
+    x.add([&] { flag0 = 1; });
+    p.setValue(42);
+    x.add([&] { flag1 = 1; });
+
+    cx.cancel();
+
+    size_t cbs = x.run();
+    EXPECT_EQ(3, cbs);
+    EXPECT_EQ(1, flag0);
+    EXPECT_EQ(0, val);
+    EXPECT_EQ(1, flag1);
+}
+
+/* The cancellation shall not affect futures without a continuation */
+TEST(CancellationFutures, withoutContinuation) {
+    InlineExecutor exe;
+    Cancellation cx;
+    Promise<unsigned> p;
+
+    auto f = p.getFuture();
+    f.via(&exe, cx);
+
+    cx.cancel();
+    p.setValue(15);
+    unsigned v = f.get();
+
+    EXPECT_EQ(15, v);
+}
+
+/* If a Cancellation is activated, if you still have the future (not
+ * destructed), you should still be able to retreive the value even
+ * though the continuation will not be invoked.
+ *
+ * NB: If you're using a continuation, it's bad practice to keep/use a
+ * reference to the original future.
+ */
+TEST(CancellationFutures, withContinuation) {
+    InlineExecutor exe;
+    Cancellation cx;
+    Promise<unsigned> p;
+    unsigned k = 0;
+
+    auto f0 = p.getFuture();
+    auto f1 = f0.via(&exe, cx).then([&] { ++k; });
+
+    cx.cancel();
+    p.setValue(29);
+
+    EXPECT_TRUE(f0.isReady());
+    ASSERT_TRUE(f0.hasValue());
+    unsigned v = f0.get();
+
+    EXPECT_FALSE(f1.isReady());
+
+    EXPECT_EQ(29, v);
+    EXPECT_EQ(0, k);
+}
+
+/* If a Cancellation via is activated, if you still have the future (not
+ * destructed), you should still be able to retreive the value even
+ * though the continuation will not be invoked.
+ * The cancellation is provided via .then call.
+ *
+ * NB: If you're using a continuation, it's bad practice to keep/use a
+ * reference to the original future.
+ */
+TEST(CancellationFutures, Then_withContinuation) {
+    InlineExecutor exe;
+    Cancellation cx;
+    Promise<unsigned> p;
+    unsigned k = 0;
+
+    auto f0 = p.getFuture();
+    auto f1 = f0.then(&exe, cx, [&] { ++k; });
+
+    cx.cancel();
+    p.setValue(29);
+
+    EXPECT_TRUE(f0.isReady());
+    ASSERT_TRUE(f0.hasValue());
+    unsigned v = f0.get();
+
+    EXPECT_FALSE(f1.isReady());
+
+    EXPECT_EQ(29, v);
+    EXPECT_EQ(0, k);
+}
+
+/* If a Cancellation is activated, values shall not leak
+ */
+TEST(CancellationFutures, valueLeakCheck) {
+    using std::shared_ptr;
+
+    struct my_value {
+        unsigned& ctor_count_;
+        unsigned& dtor_count_;
+
+        my_value() = delete;
+
+        my_value(unsigned& ctor, unsigned& dtor) :
+            ctor_count_(ctor),
+            dtor_count_(dtor)
+        {
+            ++ctor_count_;
+        }
+
+        my_value(const my_value& o) :
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        my_value(my_value&& o) :
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        ~my_value()
+        {
+            ++dtor_count_;
+        }
+    };
+
+    InlineExecutor exe;
+    Cancellation cx;
+    unsigned ctors = 0, dtors = 0;
+
+    {
+        my_value v(ctors, dtors);
+        Promise<my_value> p;
+
+        p.getFuture()
+            .via(&exe, cx)
+            .then([&] { EXPECT_TRUE(false); });
+
+        cx.cancel();
+
+        p.setValue(v);
+    }
+
+    EXPECT_LE(1, ctors);
+    EXPECT_LE(1, dtors);
+    EXPECT_EQ(ctors, dtors);
+}
+
+/* If a Cancellation is activated, values shall not leak
+ * The Cancellation is provided via .then call.
+ */
+TEST(CancellationFutures, Then_valueLeakCheck) {
+    using std::shared_ptr;
+
+    struct my_value {
+        unsigned& ctor_count_;
+        unsigned& dtor_count_;
+
+        my_value() = delete;
+
+        my_value(unsigned& ctor, unsigned& dtor) :
+            ctor_count_(ctor),
+            dtor_count_(dtor)
+        {
+            ++ctor_count_;
+        }
+
+        my_value(const my_value& o) :
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        my_value(my_value&& o) :
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        ~my_value()
+        {
+            ++dtor_count_;
+        }
+    };
+
+    InlineExecutor exe;
+    Cancellation cx;
+    unsigned ctors = 0, dtors = 0;
+
+    {
+        my_value v(ctors, dtors);
+        Promise<my_value> p;
+
+        p.getFuture()
+            .then(&exe, cx, [&] { EXPECT_TRUE(false); });
+
+        cx.cancel();
+
+        p.setValue(v);
+    }
+
+    EXPECT_LE(1, ctors);
+    EXPECT_LE(1, dtors);
+    EXPECT_EQ(ctors, dtors);
+}
+
+/* If a Cancellation is activated, exceptions shall not leak
+ */
+TEST(CancellationFutures, exceptionLeakCheck) {
+    struct my_exception : public std::exception {
+        unsigned& ctor_count_;
+        unsigned& dtor_count_;
+
+        my_exception(unsigned& ctor, unsigned& dtor) :
+            std::exception(),
+            ctor_count_(ctor),
+            dtor_count_(dtor)
+        {
+            ++ctor_count_;
+        }
+
+        my_exception(const my_exception& o) :
+            std::exception(o),
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        my_exception(my_exception&& o) :
+            std::exception(o),
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        ~my_exception()
+        {
+            ++dtor_count_;
+        }
+    };
+
+    InlineExecutor exe;
+    Cancellation cx;
+    unsigned ctors = 0, dtors = 0;
+
+    {
+        Promise<unsigned> p;
+
+        p.getFuture()
+            .via(&exe, cx)
+            .then([&] { EXPECT_TRUE(false); });
+
+        cx.cancel();
+
+        p.setException(my_exception(ctors, dtors));
+    }
+
+    EXPECT_LE(1, ctors);
+    EXPECT_LE(1, dtors);
+    EXPECT_EQ(ctors, dtors);
+}
+
+/* If a Cancellation is activated, exceptions shall not leak
+ * The Cancellation is provided via .then call
+ */
+TEST(CancellationFutures, Then_exceptionLeakCheck) {
+    struct my_exception : public std::exception {
+        unsigned& ctor_count_;
+        unsigned& dtor_count_;
+
+        my_exception(unsigned& ctor, unsigned& dtor) :
+            std::exception(),
+            ctor_count_(ctor),
+            dtor_count_(dtor)
+        {
+            ++ctor_count_;
+        }
+
+        my_exception(const my_exception& o) :
+            std::exception(o),
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        my_exception(my_exception&& o) :
+            std::exception(o),
+            ctor_count_(o.ctor_count_),
+            dtor_count_(o.dtor_count_)
+        {
+            ++ctor_count_;
+        }
+
+        ~my_exception()
+        {
+            ++dtor_count_;
+        }
+    };
+
+    InlineExecutor exe;
+    Cancellation cx;
+    unsigned ctors = 0, dtors = 0;
+
+    {
+        Promise<unsigned> p;
+
+        p.getFuture()
+            .then(&exe, cx, [&] { EXPECT_TRUE(false); });
+
+        cx.cancel();
+
+        p.setException(my_exception(ctors, dtors));
+    }
+
+    EXPECT_LE(1, ctors);
+    EXPECT_LE(1, dtors);
+    EXPECT_EQ(ctors, dtors);
+}
+
+/* A simple inline executor that supports cancellations */
+class SimpleExecutorWithCancellation : public ExecutorWithCancellation
+{
+public:
+    virtual void add(folly::Func f) {
+        f();
+    }
+
+    void setCancellation(Cancellation cx) override {
+        cx_ = std::move(cx);
+    }
+
+    Cancellation& getCancellation() override {
+        return cx_;
+    }
+
+private:
+    Cancellation cx_;
+};
+
+/* This is the primary expected use-case, using Executor's implicit cancellation */
+TEST(CancellationFutures, executorWithCancellationViaOverrideDefaultCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+
+        auto f0 = p0.getFuture()
+            .via(&x)
+            .then([&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .via(&x)
+            .then([&k1] (unsigned v) { k1 = v; });
+
+        x.getCancellation().cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(0, k1); /* cancelled */
+}
+
+/* Using Executor's implicit cancellation
+ * The Cancellation is provided via .then call.
+ */
+TEST(CancellationFutures, executorWithCancellationThenOverrideDefaultCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+
+        auto f0 = p0.getFuture()
+            .then(&x, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .then(&x, [&k1] (unsigned v) { k1 = v; });
+
+        x.getCancellation().cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(0, k1); /* cancelled */
+}
+
+/* Using Executor's implicit cancellation
+ * The Cancellation is provided via .thenMultiWithExecutor call.
+ */
+TEST(CancellationFutures, executorWithCancellationThenMultiOverrideDefaultCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+
+        auto f0 = p0.getFuture()
+            .thenMultiWithExecutor(&x, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .thenMultiWithExecutor(&x, [] (unsigned v) { return v; }, [&k1](unsigned v) {k1 = v;});
+
+        x.getCancellation().cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(0, k1); /* cancelled */
+}
+
+TEST(CancellationFutures, executorWithCancellationViaOverrideExplicitCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+        Cancellation cx;
+
+        x.setCancellation(cx);
+
+        auto f0 = p0.getFuture()
+            .via(&x)
+            .then([&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .via(&x, cx)
+            .then([&k1] (unsigned v) { k1 = v; });
+
+        x.getCancellation().cancel();
+        EXPECT_TRUE(cx.is_cancelled());
+        EXPECT_TRUE(x.getCancellation().is_cancelled());
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(0, k1); /* cancelled */
+}
+
+TEST(CancellationFutures, executorWithCancellationThenOverrideExplicitCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+        Cancellation cx;
+
+        x.setCancellation(cx);
+
+        auto f0 = p0.getFuture()
+            .then(&x, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .then(&x, cx, [&k1] (unsigned v) { k1 = v; });
+
+        x.getCancellation().cancel();
+        EXPECT_TRUE(cx.is_cancelled());
+        EXPECT_TRUE(x.getCancellation().is_cancelled());
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(0, k1); /* cancelled */
+}
+
+TEST(CancellationFutures, executorWithCancellationThenMultiOverrideExplicitCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0, k2 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1, p2;
+        SimpleExecutorWithCancellation x;
+        Cancellation cx, cx1;
+
+        x.setCancellation(cx);
+
+        auto f0 = p0.getFuture()
+            .thenMultiWithExecutor(&x, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .thenMultiWithExecutor(&x, cx, [&k1] (unsigned v) { k1 = v; });
+        auto f2 = p2.getFuture()
+            .thenMultiWithExecutor(&x, cx1, [] (unsigned v) { return v; }, [&k2] (unsigned v) {k2=v;});
+
+        x.getCancellation().cancel();
+        EXPECT_TRUE(cx.is_cancelled());
+        EXPECT_TRUE(x.getCancellation().is_cancelled());
+
+        p0.setValue(7);
+        p1.setValue(9);
+        p2.setValue(11);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(0, k1); /* cancelled */
+    EXPECT_EQ(11, k2); /* cancelled */
+}
+
+/* If e is an ExecutorWithCancellation*, .via(e) should be an alias
+ * for .via(e, e.getCancellation()).  If we use .via(e, cx) to use a
+ * /different/ cancellation, we should prefer that cancellation.
+ */
+TEST(CancellationFutures, executorWithCancellationDoesntBreakExplicitCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+        Cancellation cx0, cx1;
+
+        x.setCancellation(cx0);
+
+        auto f0 = p0.getFuture()
+            .via(&x, cx0)
+            .then([&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .via(&x, cx1)
+            .then([&k1] (unsigned v) { k1 = v; });
+
+        cx0.cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(9, k1); /* not cancelled */
+}
+
+/* If e is an ExecutorWithCancellation*, .then(e) should be an alias
+ * for .then(e, e.getCancellation()).  If we use .then(e, cx) to use a
+ * /different/ cancellation, we should prefer that cancellation.
+ */
+TEST(CancellationFutures, Then_executorWithCancellationDoesntBreakExplicitCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+        Cancellation cx0, cx1;
+
+        x.setCancellation(cx0);
+
+        auto f0 = p0.getFuture()
+            .then(&x, cx0, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .then(&x, cx1, [&k1] (unsigned v) { k1 = v; });
+
+        cx0.cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(9, k1); /* not cancelled */
+}
+
+/* If e is an ExecutorWithCancellation*, .thenMultiWithExecutor(e)
+ * should be an alias for .thenMultiWithExecutor(e, e.getCancellation()).
+ * If we use .thenMultiWithExecutor(e, cx) to use a /different/
+ * cancellation, we should prefer that cancellation.
+ */
+TEST(CancellationFutures, ThenMulti_executorWithCancellationDoesntBreakExplicitCx)
+{
+    /* code copied from "basicOperation" test */
+    unsigned k0 = 0, k1 = 0;
+
+    {
+        /* putting in a block so all destructors are called */
+        Promise<unsigned> p0, p1;
+        SimpleExecutorWithCancellation x;
+        Cancellation cx0, cx1;
+
+        x.setCancellation(cx0);
+
+        auto f0 = p0.getFuture()
+            .thenMultiWithExecutor(&x, cx0, [&k0] (unsigned v) { k0 = v; });
+        auto f1 = p1.getFuture()
+            .thenMultiWithExecutor(&x, cx1, [](unsigned v){return v;}, [&k1] (unsigned v) { k1 = v; });
+
+        cx0.cancel();
+
+        p0.setValue(7);
+        p1.setValue(9);
+    }
+
+    EXPECT_EQ(0, k0); /* cancelled */
+    EXPECT_EQ(9, k1); /* not cancelled */
+}
diff --git a/faux-folly/folly/futures/test/CollectTest.cpp b/faux-folly/folly/futures/test/CollectTest.cpp
new file mode 100644
index 0000000..25c96ee
--- /dev/null
+++ b/faux-folly/folly/futures/test/CollectTest.cpp
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <boost/thread/barrier.hpp>
+
+#include <folly/futures/Future.h>
+#include <folly/Random.h>
+
+using namespace folly;
+
+typedef FutureException eggs_t;
+static eggs_t eggs("eggs");
+
+auto rng = std::mt19937(folly::randomNumberSeed());
+
+TEST(Collect, collectAll) {
+  // returns a vector variant
+  {
+    std::vector<Promise<int>> promises(10);
+    std::vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collectAll(futures);
+
+    std::shuffle(promises.begin(), promises.end(), rng);
+    for (auto& p : promises) {
+      EXPECT_FALSE(allf.isReady());
+      p.setValue(42);
+    }
+
+    EXPECT_TRUE(allf.isReady());
+    auto& results = allf.value();
+    for (auto& t : results) {
+      EXPECT_EQ(42, t.value());
+    }
+  }
+
+  // check error semantics
+  {
+    std::vector<Promise<int>> promises(4);
+    std::vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collectAll(futures);
+
+
+    promises[0].setValue(42);
+    promises[1].setException(eggs);
+
+    EXPECT_FALSE(allf.isReady());
+
+    promises[2].setValue(42);
+
+    EXPECT_FALSE(allf.isReady());
+
+    promises[3].setException(eggs);
+
+    EXPECT_TRUE(allf.isReady());
+    EXPECT_FALSE(allf.getTry().hasException());
+
+    auto& results = allf.value();
+    EXPECT_EQ(42, results[0].value());
+    EXPECT_TRUE(results[1].hasException());
+    EXPECT_EQ(42, results[2].value());
+    EXPECT_TRUE(results[3].hasException());
+  }
+
+  // check that futures are ready in then()
+  {
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collectAll(futures)
+      .then([](Try<std::vector<Try<Unit>>>&& ts) {
+        for (auto& f : ts.value())
+          f.value();
+      });
+
+    std::shuffle(promises.begin(), promises.end(), rng);
+    for (auto& p : promises)
+      p.setValue();
+    EXPECT_TRUE(allf.isReady());
+  }
+}
+
+TEST(Collect, collect) {
+  // success case
+  {
+    std::vector<Promise<int>> promises(10);
+    std::vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures);
+
+    std::shuffle(promises.begin(), promises.end(), rng);
+    for (auto& p : promises) {
+      EXPECT_FALSE(allf.isReady());
+      p.setValue(42);
+    }
+
+    EXPECT_TRUE(allf.isReady());
+    for (auto i : allf.value()) {
+      EXPECT_EQ(42, i);
+    }
+  }
+
+  // failure case
+  {
+    std::vector<Promise<int>> promises(10);
+    std::vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures);
+
+    std::shuffle(promises.begin(), promises.end(), rng);
+    for (int i = 0; i < 10; i++) {
+      if (i < 5) {
+        // everthing goes well so far...
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setValue(42);
+      } else if (i == 5) {
+        // short circuit with an exception
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setException(eggs);
+        EXPECT_TRUE(allf.isReady());
+      } else if (i < 8) {
+        // don't blow up on further values
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setValue(42);
+      } else {
+        // don't blow up on further exceptions
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setException(eggs);
+      }
+    }
+
+    EXPECT_THROW(allf.value(), eggs_t);
+  }
+
+  // void futures success case
+  {
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures);
+
+    std::shuffle(promises.begin(), promises.end(), rng);
+    for (auto& p : promises) {
+      EXPECT_FALSE(allf.isReady());
+      p.setValue();
+    }
+
+    EXPECT_TRUE(allf.isReady());
+  }
+
+  // void futures failure case
+  {
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto allf = collect(futures);
+
+    std::shuffle(promises.begin(), promises.end(), rng);
+    for (int i = 0; i < 10; i++) {
+      if (i < 5) {
+        // everthing goes well so far...
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setValue();
+      } else if (i == 5) {
+        // short circuit with an exception
+        EXPECT_FALSE(allf.isReady());
+        promises[i].setException(eggs);
+        EXPECT_TRUE(allf.isReady());
+      } else if (i < 8) {
+        // don't blow up on further values
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setValue();
+      } else {
+        // don't blow up on further exceptions
+        EXPECT_TRUE(allf.isReady());
+        promises[i].setException(eggs);
+      }
+    }
+
+    EXPECT_THROW(allf.value(), eggs_t);
+  }
+
+  // move only compiles
+  {
+    std::vector<Promise<std::unique_ptr<int>>> promises(10);
+    std::vector<Future<std::unique_ptr<int>>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    collect(futures);
+  }
+
+}
+
+struct NotDefaultConstructible {
+  NotDefaultConstructible() = delete;
+  explicit NotDefaultConstructible(int arg) : i(arg) {}
+  int i;
+};
+
+// We have a specialized implementation for non-default-constructible objects
+// Ensure that it works and preserves order
+TEST(Collect, collectNotDefaultConstructible) {
+  std::vector<Promise<NotDefaultConstructible>> promises(10);
+  std::vector<Future<NotDefaultConstructible>> futures;
+  std::vector<int> indices(10);
+  std::iota(indices.begin(), indices.end(), 0);
+  std::shuffle(indices.begin(), indices.end(), rng);
+
+  for (auto& p : promises)
+    futures.push_back(p.getFuture());
+
+  auto allf = collect(futures);
+
+  for (auto i : indices) {
+    EXPECT_FALSE(allf.isReady());
+    promises[i].setValue(NotDefaultConstructible(i));
+  }
+
+  EXPECT_TRUE(allf.isReady());
+  int i = 0;
+  for (auto val : allf.value()) {
+    EXPECT_EQ(i, val.i);
+    i++;
+  }
+}
+
+TEST(Collect, collectAny) {
+  {
+    std::vector<Promise<int>> promises(10);
+    std::vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    for (auto& f : futures) {
+      EXPECT_FALSE(f.isReady());
+    }
+
+    auto anyf = collectAny(futures);
+
+    /* futures were moved in, so these are invalid now */
+    EXPECT_FALSE(anyf.isReady());
+
+    promises[7].setValue(42);
+    EXPECT_TRUE(anyf.isReady());
+    auto& idx_fut = anyf.value();
+
+    auto i = idx_fut.first;
+    EXPECT_EQ(7, i);
+
+    auto& f = idx_fut.second;
+    EXPECT_EQ(42, f.value());
+  }
+
+  // error
+  {
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    for (auto& f : futures) {
+      EXPECT_FALSE(f.isReady());
+    }
+
+    auto anyf = collectAny(futures);
+
+    EXPECT_FALSE(anyf.isReady());
+
+    promises[3].setException(eggs);
+    EXPECT_TRUE(anyf.isReady());
+    EXPECT_TRUE(anyf.value().second.hasException());
+  }
+
+  // then()
+  {
+    std::vector<Promise<int>> promises(10);
+    std::vector<Future<int>> futures;
+
+    for (auto& p : promises)
+      futures.push_back(p.getFuture());
+
+    auto anyf = collectAny(futures)
+      .then([](std::pair<size_t, Try<int>> p) {
+        EXPECT_EQ(42, p.second.value());
+      });
+
+    promises[3].setValue(42);
+    EXPECT_TRUE(anyf.isReady());
+  }
+}
+
+
+TEST(Collect, alreadyCompleted) {
+  {
+    std::vector<Future<Unit>> fs;
+    for (int i = 0; i < 10; i++)
+      fs.push_back(makeFuture());
+
+    collectAll(fs)
+      .then([&](std::vector<Try<Unit>> ts) {
+        EXPECT_EQ(fs.size(), ts.size());
+      });
+  }
+  {
+    std::vector<Future<int>> fs;
+    for (int i = 0; i < 10; i++)
+      fs.push_back(makeFuture(i));
+
+    collectAny(fs)
+      .then([&](std::pair<size_t, Try<int>> p) {
+        EXPECT_EQ(p.first, p.second.value());
+      });
+  }
+}
+
+TEST(Collect, parallel) {
+  std::vector<Promise<int>> ps(10);
+  std::vector<Future<int>> fs;
+  for (size_t i = 0; i < ps.size(); i++) {
+    fs.emplace_back(ps[i].getFuture());
+  }
+  auto f = collect(fs);
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      ps[i].setValue(i);
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  for (size_t i = 0; i < ps.size(); i++) {
+    EXPECT_EQ(i, f.value()[i]);
+  }
+}
+
+TEST(Collect, parallelWithError) {
+  std::vector<Promise<int>> ps(10);
+  std::vector<Future<int>> fs;
+  for (size_t i = 0; i < ps.size(); i++) {
+    fs.emplace_back(ps[i].getFuture());
+  }
+  auto f = collect(fs);
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      if (i == (ps.size()/2)) {
+        ps[i].setException(eggs);
+      } else {
+        ps[i].setValue(i);
+      }
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Collect, allParallel) {
+  std::vector<Promise<int>> ps(10);
+  std::vector<Future<int>> fs;
+  for (size_t i = 0; i < ps.size(); i++) {
+    fs.emplace_back(ps[i].getFuture());
+  }
+  auto f = collectAll(fs);
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      ps[i].setValue(i);
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  for (size_t i = 0; i < ps.size(); i++) {
+    EXPECT_TRUE(f.value()[i].hasValue());
+    EXPECT_EQ(i, f.value()[i].value());
+  }
+}
+
+TEST(Collect, allParallelWithError) {
+  std::vector<Promise<int>> ps(10);
+  std::vector<Future<int>> fs;
+  for (size_t i = 0; i < ps.size(); i++) {
+    fs.emplace_back(ps[i].getFuture());
+  }
+  auto f = collectAll(fs);
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      if (i == (ps.size()/2)) {
+        ps[i].setException(eggs);
+      } else {
+        ps[i].setValue(i);
+      }
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  for (size_t i = 0; i < ps.size(); i++) {
+    if (i == (ps.size()/2)) {
+      EXPECT_THROW(f.value()[i].value(), eggs_t);
+    } else {
+      EXPECT_TRUE(f.value()[i].hasValue());
+      EXPECT_EQ(i, f.value()[i].value());
+    }
+  }
+}
+
+TEST(Collect, collectN) {
+  std::vector<Promise<Unit>> promises(10);
+  std::vector<Future<Unit>> futures;
+
+  for (auto& p : promises)
+    futures.push_back(p.getFuture());
+
+  bool flag = false;
+  size_t n = 3;
+  collectN(futures, n)
+    .then([&](std::vector<std::pair<size_t, Try<Unit>>> v) {
+      flag = true;
+      EXPECT_EQ(n, v.size());
+      for (auto& tt : v)
+        EXPECT_TRUE(tt.second.hasValue());
+    });
+
+  promises[0].setValue();
+  EXPECT_FALSE(flag);
+  promises[1].setValue();
+  EXPECT_FALSE(flag);
+  promises[2].setValue();
+  EXPECT_TRUE(flag);
+}
+
+TEST(Collect, collectAllVariadic) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  collectAll(std::move(fb), std::move(fi))
+    .then([&](std::tuple<Try<bool>, Try<int>> tup) {
+      flag = true;
+      EXPECT_TRUE(std::get<0>(tup).hasValue());
+      EXPECT_EQ(std::get<0>(tup).value(), true);
+      EXPECT_TRUE(std::get<1>(tup).hasValue());
+      EXPECT_EQ(std::get<1>(tup).value(), 42);
+    });
+  pb.setValue(true);
+  EXPECT_FALSE(flag);
+  pi.setValue(42);
+  EXPECT_TRUE(flag);
+}
+
+TEST(Collect, collectAllVariadicReferences) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  collectAll(fb, fi)
+    .then([&](std::tuple<Try<bool>, Try<int>> tup) {
+      flag = true;
+      EXPECT_TRUE(std::get<0>(tup).hasValue());
+      EXPECT_EQ(std::get<0>(tup).value(), true);
+      EXPECT_TRUE(std::get<1>(tup).hasValue());
+      EXPECT_EQ(std::get<1>(tup).value(), 42);
+    });
+  pb.setValue(true);
+  EXPECT_FALSE(flag);
+  pi.setValue(42);
+  EXPECT_TRUE(flag);
+}
+
+TEST(Collect, collectAllVariadicWithException) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  collectAll(std::move(fb), std::move(fi))
+    .then([&](std::tuple<Try<bool>, Try<int>> tup) {
+      flag = true;
+      EXPECT_TRUE(std::get<0>(tup).hasValue());
+      EXPECT_EQ(std::get<0>(tup).value(), true);
+      EXPECT_TRUE(std::get<1>(tup).hasException());
+      EXPECT_THROW(std::get<1>(tup).value(), eggs_t);
+    });
+  pb.setValue(true);
+  EXPECT_FALSE(flag);
+  pi.setException(eggs);
+  EXPECT_TRUE(flag);
+}
+
+TEST(Collect, collectVariadic) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  collect(std::move(fb), std::move(fi))
+    .then([&](std::tuple<bool, int> tup) {
+      flag = true;
+      EXPECT_EQ(std::get<0>(tup), true);
+      EXPECT_EQ(std::get<1>(tup), 42);
+    });
+  pb.setValue(true);
+  EXPECT_FALSE(flag);
+  pi.setValue(42);
+  EXPECT_TRUE(flag);
+}
+
+TEST(Collect, collectVariadicWithException) {
+  Promise<bool> pb;
+  Promise<int> pi;
+  Future<bool> fb = pb.getFuture();
+  Future<int> fi = pi.getFuture();
+  bool flag = false;
+  auto f = collect(std::move(fb), std::move(fi));
+  pb.setValue(true);
+  EXPECT_FALSE(f.isReady());
+  pi.setException(eggs);
+  EXPECT_TRUE(f.isReady());
+  EXPECT_TRUE(f.getTry().hasException());
+  EXPECT_THROW(f.get(), eggs_t);
+}
+
+TEST(Collect, collectAllNone) {
+  std::vector<Future<int>> fs;
+  auto f = collectAll(fs);
+  EXPECT_TRUE(f.isReady());
+}
diff --git a/faux-folly/folly/futures/test/EnsureTest.cpp b/faux-folly/folly/futures/test/EnsureTest.cpp
new file mode 100644
index 0000000..99ed460
--- /dev/null
+++ b/faux-folly/folly/futures/test/EnsureTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 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 <memory>
+#include <unordered_set>
+
+#include <gtest/gtest.h>
+
+#include <folly/MoveWrapper.h>
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+TEST(Ensure, basic) {
+  size_t count = 0;
+  auto cob = [&] { count++; };
+  auto f = makeFuture(42)
+    .ensure(cob)
+    .then([](int) { throw std::runtime_error("ensure"); })
+    .ensure(cob);
+
+  EXPECT_THROW(f.get(), std::runtime_error);
+  EXPECT_EQ(2, count);
+}
+
+TEST(Ensure, mutableLambda) {
+  auto set = std::make_shared<std::unordered_set<int>>();
+  set->insert(1);
+  set->insert(2);
+
+  auto f = makeFuture(4)
+    .ensure([set]() mutable { set->clear(); })
+    .then([]() { throw std::runtime_error("ensure"); });
+
+  EXPECT_EQ(0, set->size());
+  EXPECT_THROW(f.get(), std::runtime_error);
+}
diff --git a/faux-folly/folly/futures/test/ExecutorTest.cpp b/faux-folly/folly/futures/test/ExecutorTest.cpp
new file mode 100644
index 0000000..fea6e31
--- /dev/null
+++ b/faux-folly/folly/futures/test/ExecutorTest.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/InlineExecutor.h>
+#include <folly/futures/ManualExecutor.h>
+#include <folly/futures/QueuedImmediateExecutor.h>
+#include <folly/Baton.h>
+
+using namespace folly;
+
+TEST(ManualExecutor, runIsStable) {
+  ManualExecutor x;
+  size_t count = 0;
+  auto f1 = [&]() { count++; };
+  auto f2 = [&]() { x.add(f1); x.add(f1); };
+  x.add(f2);
+  x.run();
+}
+
+TEST(ManualExecutor, scheduleDur) {
+  ManualExecutor x;
+  size_t count = 0;
+  std::chrono::milliseconds dur {10};
+  x.schedule([&]{ count++; }, dur);
+  EXPECT_EQ(count, 0);
+  x.run();
+  EXPECT_EQ(count, 0);
+  x.advance(dur/2);
+  EXPECT_EQ(count, 0);
+  x.advance(dur/2);
+  EXPECT_EQ(count, 1);
+}
+
+TEST(ManualExecutor, clockStartsAt0) {
+  ManualExecutor x;
+  EXPECT_EQ(x.now(), x.now().min());
+}
+
+TEST(ManualExecutor, scheduleAbs) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.scheduleAt([&]{ count++; }, x.now() + std::chrono::milliseconds(10));
+  EXPECT_EQ(count, 0);
+  x.advance(std::chrono::milliseconds(10));
+  EXPECT_EQ(count, 1);
+}
+
+TEST(ManualExecutor, advanceTo) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.scheduleAt([&]{ count++; }, std::chrono::steady_clock::now());
+  EXPECT_EQ(count, 0);
+  x.advanceTo(std::chrono::steady_clock::now());
+  EXPECT_EQ(count, 1);
+}
+
+TEST(ManualExecutor, advanceBack) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.advance(std::chrono::microseconds(5));
+  x.schedule([&]{ count++; }, std::chrono::microseconds(6));
+  EXPECT_EQ(count, 0);
+  x.advanceTo(x.now() - std::chrono::microseconds(1));
+  EXPECT_EQ(count, 0);
+}
+
+TEST(ManualExecutor, advanceNeg) {
+  ManualExecutor x;
+  size_t count = 0;
+  x.advance(std::chrono::microseconds(5));
+  x.schedule([&]{ count++; }, std::chrono::microseconds(6));
+  EXPECT_EQ(count, 0);
+  x.advance(std::chrono::microseconds(-1));
+  EXPECT_EQ(count, 0);
+}
+
+TEST(ManualExecutor, waitForDoesNotDeadlock) {
+  ManualExecutor east, west;
+  folly::Baton<> baton;
+  auto f = makeFuture()
+    .via(&east)
+    .then([](Try<Unit>){ return makeFuture(); })
+    .via(&west);
+  std::thread t([&]{
+    baton.post();
+    west.waitFor(f);
+  });
+  baton.wait();
+  east.run();
+  t.join();
+}
+
+TEST(Executor, InlineExecutor) {
+  InlineExecutor x;
+  size_t counter = 0;
+  x.add([&]{
+    x.add([&]{
+      EXPECT_EQ(counter, 0);
+      counter++;
+    });
+    EXPECT_EQ(counter, 1);
+    counter++;
+  });
+  EXPECT_EQ(counter, 2);
+}
+
+TEST(Executor, QueuedImmediateExecutor) {
+  QueuedImmediateExecutor x;
+  size_t counter = 0;
+  x.add([&]{
+    x.add([&]{
+      EXPECT_EQ(1, counter);
+      counter++;
+    });
+    EXPECT_EQ(0, counter);
+    counter++;
+  });
+  EXPECT_EQ(2, counter);
+}
+
+TEST(Executor, Runnable) {
+  InlineExecutor x;
+  size_t counter = 0;
+  struct Runnable {
+    std::function<void()> fn;
+    void operator()() { fn(); }
+  };
+  Runnable f;
+  f.fn = [&]{ counter++; };
+  x.add(f);
+  EXPECT_EQ(counter, 1);
+}
+
+TEST(Executor, RunnablePtr) {
+  InlineExecutor x;
+  struct Runnable {
+    std::function<void()> fn;
+    void operator()() { fn(); }
+  };
+  size_t counter = 0;
+  auto fnp = std::make_shared<Runnable>();
+  fnp->fn = [&]{ counter++; };
+  x.addPtr(fnp);
+  EXPECT_EQ(counter, 1);
+}
+
+TEST(Executor, ThrowableThen) {
+  InlineExecutor x;
+  auto f = Future<Unit>().via(&x).then([](){
+    throw std::runtime_error("Faildog");
+  });
+  EXPECT_THROW(f.value(), std::exception);
+}
+
+class CrappyExecutor : public Executor {
+ public:
+  void add(Func f) override {
+    throw std::runtime_error("bad");
+  }
+};
+
+TEST(Executor, CrappyExecutor) {
+  CrappyExecutor x;
+  bool flag = false;
+  auto f = folly::via(&x).onError([&](std::runtime_error& e) {
+    EXPECT_STREQ("bad", e.what());
+    flag = true;
+  });
+  EXPECT_TRUE(flag);
+}
+
+/* Exception should break out of Executor::run() even in Future destroyed */
+TEST(Executor, exceptionsOnAbandonedFutures)
+{
+    ManualExecutor x;
+    Promise<unsigned> p;
+
+    {
+        p.getFuture().via(&x)
+            .then([&] (Try<unsigned>&& i) {
+                    i.value();
+                })
+            .onErrorThrowIn(&x);
+    }
+
+    p.setException(std::logic_error("fubar"));
+
+    EXPECT_THROW(while (x.run()) {}, std::logic_error);
+}
+
+/* Implementation of onErrorThrowIn() shall not interfere with onError()'s behavior */
+TEST(Executor, execeptionsOnAbandondedFuturesOnError)
+{
+    ManualExecutor x;
+    Promise<unsigned> p;
+    unsigned k = 0;
+
+    {
+        auto f = p.getFuture().via(&x)
+            .then([&] (unsigned i) { EXPECT_TRUE(false); })
+            .onError([&] (std::logic_error& e) { ++k; });
+    }
+
+    p.setException(std::logic_error("fubar"));
+
+    while (x.run()) {}
+
+    EXPECT_EQ(1, k);
+}
diff --git a/faux-folly/folly/futures/test/FSMTest.cpp b/faux-folly/folly/futures/test/FSMTest.cpp
new file mode 100644
index 0000000..ee28b45
--- /dev/null
+++ b/faux-folly/folly/futures/test/FSMTest.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/detail/FSM.h>
+
+using namespace folly::detail;
+
+enum class State { A, B };
+
+TEST(FSM, example) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  int unprotectedCount = 0;
+
+  // somebody set up us the switch
+  auto tryTransition = [&]{
+    switch (fsm.getState()) {
+    case State::A:
+      return fsm.updateState(State::A, State::B, [&]{ count++; });
+    case State::B:
+      return fsm.updateState(State::B, State::A,
+                             [&]{ count--; }, [&]{ unprotectedCount--; });
+    }
+    return false; // unreachable
+  };
+
+  // keep retrying until success (like a cas)
+  while (!tryTransition()) ;
+  EXPECT_EQ(State::B, fsm.getState());
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(0, unprotectedCount);
+
+  while (!tryTransition()) ;
+  EXPECT_EQ(State::A, fsm.getState());
+  EXPECT_EQ(0, count);
+  EXPECT_EQ(-1, unprotectedCount);
+}
+
+TEST(FSM, magicMacrosExample) {
+  struct MyFSM {
+    FSM<State> fsm_;
+    int count = 0;
+    int unprotectedCount = 0;
+    MyFSM() : fsm_(State::A) {}
+    void twiddle() {
+      FSM_START(fsm_)
+        FSM_CASE(fsm_, State::A, State::B, [&]{ count++; });
+        FSM_CASE2(fsm_, State::B, State::A,
+                  [&]{ count--; }, [&]{ unprotectedCount--; });
+      FSM_END
+    }
+  };
+
+  MyFSM fsm;
+
+  fsm.twiddle();
+  EXPECT_EQ(State::B, fsm.fsm_.getState());
+  EXPECT_EQ(1, fsm.count);
+  EXPECT_EQ(0, fsm.unprotectedCount);
+
+  fsm.twiddle();
+  EXPECT_EQ(State::A, fsm.fsm_.getState());
+  EXPECT_EQ(0, fsm.count);
+  EXPECT_EQ(-1, fsm.unprotectedCount);
+}
+
+
+TEST(FSM, ctor) {
+  FSM<State> fsm(State::A);
+  EXPECT_EQ(State::A, fsm.getState());
+}
+
+TEST(FSM, update) {
+  FSM<State> fsm(State::A);
+  EXPECT_TRUE(fsm.updateState(State::A, State::B, []{}));
+  EXPECT_EQ(State::B, fsm.getState());
+}
+
+TEST(FSM, badUpdate) {
+  FSM<State> fsm(State::A);
+  EXPECT_FALSE(fsm.updateState(State::B, State::A, []{}));
+}
+
+TEST(FSM, actionOnUpdate) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  fsm.updateState(State::A, State::B, [&]{ count++; });
+  EXPECT_EQ(1, count);
+}
+
+TEST(FSM, noActionOnBadUpdate) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  fsm.updateState(State::B, State::A, [&]{ count++; });
+  EXPECT_EQ(0, count);
+}
+
+TEST(FSM, stateTransitionAfterAction) {
+  FSM<State> fsm(State::A);
+  fsm.updateState(State::A, State::B,
+                  [&]{ EXPECT_EQ(State::A, fsm.getState()); });
+}
diff --git a/faux-folly/folly/futures/test/FilterTest.cpp b/faux-folly/folly/futures/test/FilterTest.cpp
new file mode 100644
index 0000000..5b37080
--- /dev/null
+++ b/faux-folly/folly/futures/test/FilterTest.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+TEST(Filter, alwaysTrye) {
+  EXPECT_EQ(42, makeFuture(42).filter([](int){ return true; }).get());
+}
+
+TEST(Filter, alwaysFalse) {
+  EXPECT_THROW(makeFuture(42).filter([](int){ return false; }).get(),
+               folly::PredicateDoesNotObtain);
+}
+
+TEST(Filter, moveOnlyValue) {
+  EXPECT_EQ(42,
+    *makeFuture(folly::make_unique<int>(42))
+     .filter([](std::unique_ptr<int> const&) { return true; })
+     .get());
+}
diff --git a/faux-folly/folly/futures/test/FutureTest.cpp b/faux-folly/folly/futures/test/FutureTest.cpp
new file mode 100644
index 0000000..3aadf0a
--- /dev/null
+++ b/faux-folly/folly/futures/test/FutureTest.cpp
@@ -0,0 +1,686 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+#include <folly/Memory.h>
+#include <folly/Executor.h>
+#include <folly/dynamic.h>
+#include <folly/Baton.h>
+
+#include <algorithm>
+#include <atomic>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <unistd.h>
+
+using namespace folly;
+
+#define EXPECT_TYPE(x, T) \
+  EXPECT_TRUE((std::is_same<decltype(x), T>::value))
+
+typedef FutureException eggs_t;
+static eggs_t eggs("eggs");
+
+// Future
+
+TEST(Future, onError) {
+  bool theFlag = false;
+  auto flag = [&]{ theFlag = true; };
+#define EXPECT_FLAG() \
+  do { \
+    EXPECT_TRUE(theFlag); \
+    theFlag = false; \
+  } while(0);
+
+#define EXPECT_NO_FLAG() \
+  do { \
+    EXPECT_FALSE(theFlag); \
+    theFlag = false; \
+  } while(0);
+
+  // By reference
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (eggs_t& e) { flag(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (eggs_t& e) { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // By value
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (eggs_t e) { flag(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (eggs_t e) { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // Polymorphic
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (std::exception& e) { flag(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (std::exception& e) { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // Non-exceptions
+  {
+    auto f = makeFuture()
+      .then([] { throw -1; })
+      .onError([&] (int e) { flag(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw -1; })
+      .onError([&] (int e) { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // Mutable lambda
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (eggs_t& e) mutable { flag(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (eggs_t& e) mutable { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // No throw
+  {
+    auto f = makeFuture()
+      .then([] { return 42; })
+      .onError([&] (eggs_t& e) { flag(); return -1; });
+    EXPECT_NO_FLAG();
+    EXPECT_EQ(42, f.value());
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { return 42; })
+      .onError([&] (eggs_t& e) { flag(); return makeFuture<int>(-1); });
+    EXPECT_NO_FLAG();
+    EXPECT_EQ(42, f.value());
+  }
+
+  // Catch different exception
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (std::runtime_error& e) { flag(); });
+    EXPECT_NO_FLAG();
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (std::runtime_error& e) { flag(); return makeFuture(); });
+    EXPECT_NO_FLAG();
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  // Returned value propagates
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; return 0; })
+      .onError([&] (eggs_t& e) { return 42; });
+    EXPECT_EQ(42, f.value());
+  }
+
+  // Returned future propagates
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; return 0; })
+      .onError([&] (eggs_t& e) { return makeFuture<int>(42); });
+    EXPECT_EQ(42, f.value());
+  }
+
+  // Throw in callback
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; return 0; })
+      .onError([&] (eggs_t& e) { throw e; return -1; });
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; return 0; })
+      .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  // exception_wrapper, return Future<T>
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (exception_wrapper e) { flag(); return makeFuture(); });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+  // exception_wrapper, return Future<T> but throw
+  {
+    auto f = makeFuture()
+      .then([]{ throw eggs; return 0; })
+      .onError([&] (exception_wrapper e) {
+        flag();
+        throw eggs;
+        return makeFuture<int>(-1);
+      });
+    EXPECT_FLAG();
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  // exception_wrapper, return T
+  {
+    auto f = makeFuture()
+      .then([]{ throw eggs; return 0; })
+      .onError([&] (exception_wrapper e) {
+        flag();
+        return -1;
+      });
+    EXPECT_FLAG();
+    EXPECT_EQ(-1, f.value());
+  }
+
+  // exception_wrapper, return T but throw
+  {
+    auto f = makeFuture()
+      .then([]{ throw eggs; return 0; })
+      .onError([&] (exception_wrapper e) {
+        flag();
+        throw eggs;
+        return -1;
+      });
+    EXPECT_FLAG();
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+
+  // const exception_wrapper&
+  {
+    auto f = makeFuture()
+      .then([] { throw eggs; })
+      .onError([&] (const exception_wrapper& e) {
+        flag();
+        return makeFuture();
+      });
+    EXPECT_FLAG();
+    EXPECT_NO_THROW(f.value());
+  }
+
+}
+
+TEST(Future, special) {
+  EXPECT_FALSE(std::is_copy_constructible<Future<int>>::value);
+  EXPECT_FALSE(std::is_copy_assignable<Future<int>>::value);
+  EXPECT_TRUE(std::is_move_constructible<Future<int>>::value);
+  EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);
+}
+
+TEST(Future, then) {
+  auto f = makeFuture<std::string>("0")
+    .then([](){
+      return makeFuture<std::string>("1"); })
+    .then([](Try<std::string>&& t) {
+      return makeFuture(t.value() + ";2"); })
+    .then([](const Try<std::string>&& t) {
+      return makeFuture(t.value() + ";3"); })
+    .then([](Try<std::string>& t) {
+      return makeFuture(t.value() + ";4"); })
+    .then([](const Try<std::string>& t) {
+      return makeFuture(t.value() + ";5"); })
+    .then([](Try<std::string> t) {
+      return makeFuture(t.value() + ";6"); })
+    .then([](const Try<std::string> t) {
+      return makeFuture(t.value() + ";7"); })
+    .then([](std::string&& s) {
+      return makeFuture(s + ";8"); })
+    .then([](const std::string&& s) {
+      return makeFuture(s + ";9"); })
+    .then([](std::string& s) {
+      return makeFuture(s + ";10"); })
+    .then([](const std::string& s) {
+      return makeFuture(s + ";11"); })
+    .then([](std::string s) {
+      return makeFuture(s + ";12"); })
+    .then([](const std::string s) {
+      return makeFuture(s + ";13"); })
+  ;
+  EXPECT_EQ(f.value(), "1;2;3;4;5;6;7;8;9;10;11;12;13");
+}
+
+TEST(Future, thenTry) {
+  bool flag = false;
+
+  makeFuture<int>(42).then([&](Try<int>&& t) {
+                              flag = true;
+                              EXPECT_EQ(42, t.value());
+                            });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture<int>(42)
+    .then([](Try<int>&& t) { return t.value(); })
+    .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture().then([&](Try<Unit>&& t) { flag = true; t.value(); });
+  EXPECT_TRUE(flag); flag = false;
+
+  Promise<Unit> p;
+  auto f = p.getFuture().then([&](Try<Unit>&& t) { flag = true; });
+  EXPECT_FALSE(flag);
+  EXPECT_FALSE(f.isReady());
+  p.setValue();
+  EXPECT_TRUE(flag);
+  EXPECT_TRUE(f.isReady());
+}
+
+TEST(Future, thenValue) {
+  bool flag = false;
+  makeFuture<int>(42).then([&](int i){
+    EXPECT_EQ(42, i);
+    flag = true;
+  });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture<int>(42)
+    .then([](int i){ return i; })
+    .then([&](int i) { flag = true; EXPECT_EQ(42, i); });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture().then([&]{
+    flag = true;
+  });
+  EXPECT_TRUE(flag); flag = false;
+
+  auto f = makeFuture<int>(eggs).then([&](int i){});
+  EXPECT_THROW(f.value(), eggs_t);
+
+  f = makeFuture<Unit>(eggs).then([&]{});
+  EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Future, thenValueFuture) {
+  bool flag = false;
+  makeFuture<int>(42)
+    .then([](int i){ return makeFuture<int>(std::move(i)); })
+    .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
+  EXPECT_TRUE(flag); flag = false;
+
+  makeFuture()
+    .then([]{ return makeFuture(); })
+    .then([&](Try<Unit>&& t) { flag = true; });
+  EXPECT_TRUE(flag); flag = false;
+}
+
+static std::string doWorkStatic(Try<std::string>&& t) {
+  return t.value() + ";static";
+}
+
+TEST(Future, thenFunction) {
+  struct Worker {
+    std::string doWork(Try<std::string>&& t) {
+      return t.value() + ";class";
+    }
+    static std::string doWorkStatic(Try<std::string>&& t) {
+      return t.value() + ";class-static";
+    }
+  } w;
+
+  auto f = makeFuture<std::string>("start")
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&Worker::doWork, &w);
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+static Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {
+  return makeFuture(t.value() + ";static");
+}
+
+TEST(Future, thenFunctionFuture) {
+  struct Worker {
+    Future<std::string> doWorkFuture(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class");
+    }
+    static Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = makeFuture<std::string>("start")
+    .then(doWorkStaticFuture)
+    .then(Worker::doWorkStaticFuture)
+    .then(&Worker::doWorkFuture, &w);
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+TEST(Future, thenStdFunction) {
+  {
+    std::function<int()> fn = [](){ return 42; };
+    auto f = makeFuture().then(std::move(fn));
+    EXPECT_EQ(f.value(), 42);
+  }
+  {
+    std::function<int(int)> fn = [](int i){ return i + 23; };
+    auto f = makeFuture(19).then(std::move(fn));
+    EXPECT_EQ(f.value(), 42);
+  }
+  {
+    std::function<int(Try<int>&)> fn = [](Try<int>& t){ return t.value() + 2; };
+    auto f = makeFuture(1).then(std::move(fn));
+    EXPECT_EQ(f.value(), 3);
+  }
+  {
+    bool flag = false;
+    std::function<void()> fn = [&flag](){ flag = true; };
+    auto f = makeFuture().then(std::move(fn));
+    EXPECT_TRUE(f.isReady());
+    EXPECT_TRUE(flag);
+  }
+}
+
+TEST(Future, thenBind) {
+  auto l = []() {
+    return makeFuture("bind");
+  };
+  auto b = std::bind(l);
+  auto f = makeFuture().then(std::move(b));
+  EXPECT_EQ(f.value(), "bind");
+}
+
+TEST(Future, thenBindTry) {
+  auto l = [](Try<std::string>&& t) {
+    return makeFuture(t.value() + ";bind");
+  };
+  auto b = std::bind(l, std::placeholders::_1);
+  auto f = makeFuture<std::string>("start").then(std::move(b));
+
+  EXPECT_EQ(f.value(), "start;bind");
+}
+
+TEST(Future, value) {
+  auto f = makeFuture(std::unique_ptr<int>(new int(42)));
+  auto up = std::move(f.value());
+  EXPECT_EQ(42, *up);
+
+  EXPECT_THROW(makeFuture<int>(eggs).value(), eggs_t);
+}
+
+TEST(Future, isReady) {
+  Promise<int> p;
+  auto f = p.getFuture();
+  EXPECT_FALSE(f.isReady());
+  p.setValue(42);
+  EXPECT_TRUE(f.isReady());
+  }
+
+TEST(Future, futureNotReady) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Future, hasException) {
+  EXPECT_TRUE(makeFuture<int>(eggs).getTry().hasException());
+  EXPECT_FALSE(makeFuture(42).getTry().hasException());
+}
+
+TEST(Future, hasValue) {
+  EXPECT_TRUE(makeFuture(42).getTry().hasValue());
+  EXPECT_FALSE(makeFuture<int>(eggs).getTry().hasValue());
+}
+
+TEST(Future, makeFuture) {
+  EXPECT_TYPE(makeFuture(42), Future<int>);
+  EXPECT_EQ(42, makeFuture(42).value());
+
+  EXPECT_TYPE(makeFuture<float>(42), Future<float>);
+  EXPECT_EQ(42, makeFuture<float>(42).value());
+
+  auto fun = [] { return 42; };
+  EXPECT_TYPE(makeFutureWith(fun), Future<int>);
+  EXPECT_EQ(42, makeFutureWith(fun).value());
+
+  auto failfun = []() -> int { throw eggs; };
+  EXPECT_TYPE(makeFutureWith(failfun), Future<int>);
+  EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t);
+
+  EXPECT_TYPE(makeFuture(), Future<Unit>);
+}
+
+TEST(Future, finish) {
+  auto x = std::make_shared<int>(0);
+  {
+    Promise<int> p;
+    auto f = p.getFuture().then([x](Try<int>&& t) { *x = t.value(); });
+
+    // The callback hasn't executed
+    EXPECT_EQ(0, *x);
+
+    // The callback has a reference to x
+    EXPECT_EQ(2, x.use_count());
+
+    p.setValue(42);
+
+    // the callback has executed
+    EXPECT_EQ(42, *x);
+  }
+  // the callback has been destructed
+  // and has released its reference to x
+  EXPECT_EQ(1, x.use_count());
+}
+
+TEST(Future, unwrap) {
+  Promise<int> a;
+  Promise<int> b;
+
+  auto fa = a.getFuture();
+  auto fb = b.getFuture();
+
+  bool flag1 = false;
+  bool flag2 = false;
+
+  // do a, then do b, and get the result of a + b.
+  Future<int> f = fa.then([&](Try<int>&& ta) {
+    auto va = ta.value();
+    flag1 = true;
+    return fb.then([va, &flag2](Try<int>&& tb) {
+      flag2 = true;
+      return va + tb.value();
+    });
+  });
+
+  EXPECT_FALSE(flag1);
+  EXPECT_FALSE(flag2);
+  EXPECT_FALSE(f.isReady());
+
+  a.setValue(3);
+  EXPECT_TRUE(flag1);
+  EXPECT_FALSE(flag2);
+  EXPECT_FALSE(f.isReady());
+
+  b.setValue(4);
+  EXPECT_TRUE(flag1);
+  EXPECT_TRUE(flag2);
+  EXPECT_EQ(7, f.value());
+}
+
+TEST(Future, throwCaughtInImmediateThen) {
+  // Neither of these should throw "Promise already satisfied"
+  makeFuture().then(
+    [=](Try<Unit>&&) -> int { throw std::exception(); });
+  makeFuture().then(
+    [=](Try<Unit>&&) -> Future<int> { throw std::exception(); });
+}
+
+TEST(Future, throwIfFailed) {
+  makeFuture<Unit>(eggs)
+    .then([=](Try<Unit>&& t) {
+      EXPECT_THROW(t.throwIfFailed(), eggs_t);
+    });
+  makeFuture()
+    .then([=](Try<Unit>&& t) {
+      EXPECT_NO_THROW(t.throwIfFailed());
+    });
+
+  makeFuture<int>(eggs)
+    .then([=](Try<int>&& t) {
+      EXPECT_THROW(t.throwIfFailed(), eggs_t);
+    });
+  makeFuture<int>(42)
+    .then([=](Try<int>&& t) {
+      EXPECT_NO_THROW(t.throwIfFailed());
+    });
+}
+
+TEST(Future, getFutureAfterSetValue) {
+  Promise<int> p;
+  p.setValue(42);
+  EXPECT_EQ(42, p.getFuture().value());
+}
+
+TEST(Future, getFutureAfterSetException) {
+  Promise<Unit> p;
+  p.setWith([]() -> void { throw std::logic_error("foo"); });
+  EXPECT_THROW(p.getFuture().value(), std::logic_error);
+}
+
+TEST(Future, detachRace) {
+  // Task #5438209
+  // This test is designed to detect a race that was in Core::detachOne()
+  // where detached_ was incremented and then tested, and that
+  // allowed a race where both Promise and Future would think they were the
+  // second and both try to delete. This showed up at scale but was very
+  // difficult to reliably repro in a test. As it is, this only fails about
+  // once in every 1,000 executions. Doing this 1,000 times is going to make a
+  // slow test so I won't do that but if it ever fails, take it seriously, and
+  // run the test binary with "--gtest_repeat=10000 --gtest_filter=*detachRace"
+  // (Don't forget to enable ASAN)
+  auto p = folly::make_unique<Promise<bool>>();
+  auto f = folly::make_unique<Future<bool>>(p->getFuture());
+  folly::Baton<> baton;
+  std::thread t1([&]{
+    baton.post();
+    p.reset();
+  });
+  baton.wait();
+  f.reset();
+  t1.join();
+}
+
+// Test of handling of a circular dependency. It's never recommended
+// to have one because of possible memory leaks. Here we test that
+// we can handle freeing of the Future while it is running.
+TEST(Future, CircularDependencySharedPtrSelfReset) {
+  Promise<int64_t> promise;
+  auto ptr = std::make_shared<Future<int64_t>>(promise.getFuture());
+
+  ptr->then(
+    [ptr] (folly::Try<int64_t>&& uid) mutable {
+      EXPECT_EQ(1, ptr.use_count());
+
+      // Leaving no references to ourselves.
+      ptr.reset();
+      EXPECT_EQ(0, ptr.use_count());
+    }
+  );
+
+  EXPECT_EQ(2, ptr.use_count());
+
+  ptr.reset();
+
+  promise.setValue(1);
+}
+
+TEST(Future, Constructor) {
+  auto f1 = []() -> Future<int> { return Future<int>(3); }();
+  EXPECT_EQ(f1.value(), 3);
+  auto f2 = []() -> Future<Unit> { return Future<Unit>(); }();
+  EXPECT_NO_THROW(f2.value());
+}
+
+TEST(Future, ImplicitConstructor) {
+  auto f1 = []() -> Future<int> { return 3; }();
+  EXPECT_EQ(f1.value(), 3);
+  // Unfortunately, the C++ standard does not allow the
+  // following implicit conversion to work:
+  //auto f2 = []() -> Future<Unit> { }();
+}
+
+TEST(Future, thenDynamic) {
+  // folly::dynamic has a constructor that takes any T, this test makes
+  // sure that we call the then lambda with folly::dynamic and not
+  // Try<folly::dynamic> because that then fails to compile
+  Promise<folly::dynamic> p;
+  Future<folly::dynamic> f = p.getFuture().then(
+      [](const folly::dynamic& d) {
+        return folly::dynamic(d.asInt() + 3);
+      }
+  );
+  p.setValue(2);
+  EXPECT_EQ(f.get(), 5);
+}
+
+TEST(Future, makeFutureNoThrow) {
+  makeFuture().value();
+}
diff --git a/faux-folly/folly/futures/test/HeaderCompileTest.cpp b/faux-folly/folly/futures/test/HeaderCompileTest.cpp
new file mode 100644
index 0000000..2138bcc
--- /dev/null
+++ b/faux-folly/folly/futures/test/HeaderCompileTest.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+// amazing what things can go wrong if you include things in an unexpected
+// order.
+#include <folly/futures/Try.h>
+#include <folly/futures/Promise.h>
+#include <folly/futures/Future.h>
+
+TEST(Basic, compiles) {
+  EXPECT_TRUE(true);
+}
diff --git a/faux-folly/folly/futures/test/InterruptTest.cpp b/faux-folly/folly/futures/test/InterruptTest.cpp
new file mode 100644
index 0000000..65f169e
--- /dev/null
+++ b/faux-folly/folly/futures/test/InterruptTest.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+#include <folly/Baton.h>
+
+using namespace folly;
+
+TEST(Interrupt, raise) {
+  std::runtime_error eggs("eggs");
+  Promise<Unit> p;
+  p.setInterruptHandler([&](const exception_wrapper& e) {
+    EXPECT_THROW(e.throwException(), decltype(eggs));
+  });
+  p.getFuture().raise(eggs);
+}
+
+TEST(Interrupt, cancel) {
+  Promise<Unit> p;
+  p.setInterruptHandler([&](const exception_wrapper& e) {
+    EXPECT_THROW(e.throwException(), FutureCancellation);
+  });
+  p.getFuture().cancel();
+}
+
+TEST(Interrupt, handleThenInterrupt) {
+  Promise<int> p;
+  bool flag = false;
+  p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
+  p.getFuture().cancel();
+  EXPECT_TRUE(flag);
+}
+
+TEST(Interrupt, interruptThenHandle) {
+  Promise<int> p;
+  bool flag = false;
+  p.getFuture().cancel();
+  p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
+  EXPECT_TRUE(flag);
+}
+
+TEST(Interrupt, interruptAfterFulfilNoop) {
+  Promise<Unit> p;
+  bool flag = false;
+  p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
+  p.setValue();
+  p.getFuture().cancel();
+  EXPECT_FALSE(flag);
+}
+
+TEST(Interrupt, secondInterruptNoop) {
+  Promise<Unit> p;
+  int count = 0;
+  p.setInterruptHandler([&](const exception_wrapper& e) { count++; });
+  auto f = p.getFuture();
+  f.cancel();
+  f.cancel();
+  EXPECT_EQ(1, count);
+}
+
+TEST(Interrupt, withinTimedOut) {
+  Promise<int> p;
+  Baton<> done;
+  p.setInterruptHandler([&](const exception_wrapper& e) { done.post(); });
+  p.getFuture().within(std::chrono::milliseconds(1));
+  // Give it 100ms to time out and call the interrupt handler
+  auto t = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
+  EXPECT_TRUE(done.timed_wait(t));
+}
diff --git a/faux-folly/folly/futures/test/MapTest.cpp b/faux-folly/folly/futures/test/MapTest.cpp
new file mode 100644
index 0000000..ec75bf8
--- /dev/null
+++ b/faux-folly/folly/futures/test/MapTest.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+TEST(Map, basic) {
+  Promise<int> p1;
+  Promise<int> p2;
+  Promise<int> p3;
+
+  std::vector<Future<int>> fs;
+  fs.push_back(p1.getFuture());
+  fs.push_back(p2.getFuture());
+  fs.push_back(p3.getFuture());
+
+  int c = 0;
+  std::vector<Future<Unit>> fs2 = futures::map(fs, [&](int i){
+    c += i;
+  });
+
+  // Ensure we call the callbacks as the futures complete regardless of order
+  p2.setValue(1);
+  EXPECT_EQ(1, c);
+  p3.setValue(1);
+  EXPECT_EQ(2, c);
+  p1.setValue(1);
+  EXPECT_EQ(3, c);
+
+  EXPECT_TRUE(collect(fs2).isReady());
+}
diff --git a/faux-folly/folly/futures/test/PollTest.cpp b/faux-folly/folly/futures/test/PollTest.cpp
new file mode 100644
index 0000000..d241b4e
--- /dev/null
+++ b/faux-folly/folly/futures/test/PollTest.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+TEST(Poll, ready) {
+  Promise<int> p;
+  auto f = p.getFuture();
+  p.setValue(42);
+  EXPECT_EQ(42, f.poll().value().value());
+}
+
+TEST(Poll, notReady) {
+  Promise<int> p;
+  auto f = p.getFuture();
+  EXPECT_FALSE(f.poll().hasValue());
+}
+
+TEST(Poll, exception) {
+  Promise<Unit> p;
+  auto f = p.getFuture();
+  p.setWith([] { throw std::runtime_error("Runtime"); });
+  EXPECT_TRUE(f.poll().value().hasException());
+}
diff --git a/faux-folly/folly/futures/test/PromiseTest.cpp b/faux-folly/folly/futures/test/PromiseTest.cpp
new file mode 100644
index 0000000..6338d68
--- /dev/null
+++ b/faux-folly/folly/futures/test/PromiseTest.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+using std::unique_ptr;
+using std::string;
+
+typedef FutureException eggs_t;
+static eggs_t eggs("eggs");
+
+TEST(Promise, special) {
+  EXPECT_FALSE(std::is_copy_constructible<Promise<int>>::value);
+  EXPECT_FALSE(std::is_copy_assignable<Promise<int>>::value);
+  EXPECT_TRUE(std::is_move_constructible<Promise<int>>::value);
+  EXPECT_TRUE(std::is_move_assignable<Promise<int>>::value);
+}
+
+TEST(Promise, getFuture) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  EXPECT_FALSE(f.isReady());
+}
+
+TEST(Promise, setValue) {
+  Promise<int> fund;
+  auto ffund = fund.getFuture();
+  fund.setValue(42);
+  EXPECT_EQ(42, ffund.value());
+
+  struct Foo {
+    string name;
+    int value;
+  };
+
+  Promise<Foo> pod;
+  auto fpod = pod.getFuture();
+  Foo f = {"the answer", 42};
+  pod.setValue(f);
+  Foo f2 = fpod.value();
+  EXPECT_EQ(f.name, f2.name);
+  EXPECT_EQ(f.value, f2.value);
+
+  pod = Promise<Foo>();
+  fpod = pod.getFuture();
+  pod.setValue(std::move(f2));
+  Foo f3 = fpod.value();
+  EXPECT_EQ(f.name, f3.name);
+  EXPECT_EQ(f.value, f3.value);
+
+  Promise<unique_ptr<int>> mov;
+  auto fmov = mov.getFuture();
+  mov.setValue(unique_ptr<int>(new int(42)));
+  unique_ptr<int> ptr = std::move(fmov.value());
+  EXPECT_EQ(42, *ptr);
+
+  Promise<Unit> v;
+  auto fv = v.getFuture();
+  v.setValue();
+  EXPECT_TRUE(fv.isReady());
+}
+
+TEST(Promise, setException) {
+  {
+    Promise<Unit> p;
+    auto f = p.getFuture();
+    p.setException(eggs);
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+  {
+    Promise<Unit> p;
+    auto f = p.getFuture();
+    try {
+      throw eggs;
+    } catch (...) {
+      p.setException(exception_wrapper(std::current_exception()));
+    }
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+}
+
+TEST(Promise, setWith) {
+  {
+    Promise<int> p;
+    auto f = p.getFuture();
+    p.setWith([] { return 42; });
+    EXPECT_EQ(42, f.value());
+  }
+  {
+    Promise<int> p;
+    auto f = p.getFuture();
+    p.setWith([]() -> int { throw eggs; });
+    EXPECT_THROW(f.value(), eggs_t);
+  }
+}
+
+TEST(Promise, isFulfilled) {
+  Promise<int> p;
+
+  EXPECT_FALSE(p.isFulfilled());
+  p.setValue(42);
+  EXPECT_TRUE(p.isFulfilled());
+}
+
+TEST(Promise, isFulfilledWithFuture) {
+  Promise<int> p;
+  auto f = p.getFuture(); // so core_ will become null
+
+  EXPECT_FALSE(p.isFulfilled());
+  p.setValue(42); // after here
+  EXPECT_TRUE(p.isFulfilled());
+}
diff --git a/faux-folly/folly/futures/test/ReduceTest.cpp b/faux-folly/folly/futures/test/ReduceTest.cpp
new file mode 100644
index 0000000..20e2924
--- /dev/null
+++ b/faux-folly/folly/futures/test/ReduceTest.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+TEST(Reduce, basic) {
+  auto makeFutures = [](int count) {
+    std::vector<Future<int>> fs;
+    for (int i = 1; i <= count; ++i) {
+      fs.emplace_back(makeFuture(i));
+    }
+    return fs;
+  };
+
+  // Empty (Try)
+  {
+    auto fs = makeFutures(0);
+
+    Future<double> f1 = reduce(fs, 1.2,
+      [](double a, Try<int>&& b){
+        return a + *b + 0.1;
+      });
+    EXPECT_EQ(1.2, f1.get());
+  }
+
+  // One (Try)
+  {
+    auto fs = makeFutures(1);
+
+    Future<double> f1 = reduce(fs, 0.0,
+      [](double a, Try<int>&& b){
+        return a + *b + 0.1;
+      });
+    EXPECT_EQ(1.1, f1.get());
+  }
+
+  // Returning values (Try)
+  {
+    auto fs = makeFutures(3);
+
+    Future<double> f1 = reduce(fs, 0.0,
+      [](double a, Try<int>&& b){
+        return a + *b + 0.1;
+      });
+    EXPECT_EQ(6.3, f1.get());
+  }
+
+  // Returning values
+  {
+    auto fs = makeFutures(3);
+
+    Future<double> f1 = reduce(fs, 0.0,
+      [](double a, int&& b){
+        return a + b + 0.1;
+      });
+    EXPECT_EQ(6.3, f1.get());
+  }
+
+  // Returning futures (Try)
+  {
+    auto fs = makeFutures(3);
+
+    Future<double> f2 = reduce(fs, 0.0,
+      [](double a, Try<int>&& b){
+        return makeFuture<double>(a + *b + 0.1);
+      });
+    EXPECT_EQ(6.3, f2.get());
+  }
+
+  // Returning futures
+  {
+    auto fs = makeFutures(3);
+
+    Future<double> f2 = reduce(fs, 0.0,
+      [](double a, int&& b){
+        return makeFuture<double>(a + b + 0.1);
+      });
+    EXPECT_EQ(6.3, f2.get());
+  }
+}
+
+TEST(Reduce, chain) {
+  auto makeFutures = [](int count) {
+    std::vector<Future<int>> fs;
+    for (int i = 1; i <= count; ++i) {
+      fs.emplace_back(makeFuture(i));
+    }
+    return fs;
+  };
+
+  {
+    auto f = collectAll(makeFutures(3)).reduce(0, [](int a, Try<int>&& b){
+      return a + *b;
+    });
+    EXPECT_EQ(6, f.get());
+  }
+  {
+    auto f = collect(makeFutures(3)).reduce(0, [](int a, int&& b){
+      return a + b;
+    });
+    EXPECT_EQ(6, f.get());
+  }
+}
+
+TEST(Reduce, unorderedReduce) {
+  {
+    std::vector<Future<int>> fs;
+    fs.push_back(makeFuture(1));
+    fs.push_back(makeFuture(2));
+    fs.push_back(makeFuture(3));
+
+    Future<double> f = unorderedReduce(fs.begin(), fs.end(), 0.0,
+      [](double a, int&& b){
+        return double(b);
+      });
+    EXPECT_EQ(3.0, f.get());
+  }
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    Promise<int> p3;
+
+    std::vector<Future<int>> fs;
+    fs.push_back(p1.getFuture());
+    fs.push_back(p2.getFuture());
+    fs.push_back(p3.getFuture());
+
+    Future<double> f = unorderedReduce(fs.begin(), fs.end(), 0.0,
+      [](double a, int&& b){
+        return double(b);
+      });
+    p3.setValue(3);
+    p2.setValue(2);
+    p1.setValue(1);
+    EXPECT_EQ(1.0, f.get());
+  }
+}
+
+TEST(Reduce, unorderedReduceException) {
+  Promise<int> p1;
+  Promise<int> p2;
+  Promise<int> p3;
+
+  std::vector<Future<int>> fs;
+  fs.push_back(p1.getFuture());
+  fs.push_back(p2.getFuture());
+  fs.push_back(p3.getFuture());
+
+  Future<double> f = unorderedReduce(fs.begin(), fs.end(), 0.0,
+    [](double a, int&& b){
+      return b + 0.0;
+    });
+  p3.setValue(3);
+  p2.setException(exception_wrapper(std::runtime_error("blah")));
+  p1.setValue(1);
+  EXPECT_THROW(f.get(), std::runtime_error);
+}
diff --git a/faux-folly/folly/futures/test/RetryingTest.cpp b/faux-folly/folly/futures/test/RetryingTest.cpp
new file mode 100644
index 0000000..50edee1
--- /dev/null
+++ b/faux-folly/folly/futures/test/RetryingTest.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <exception>
+#include <vector>
+#include <folly/futures/Future.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace std::chrono;
+using namespace folly;
+
+// Runs func num_times in parallel, expects that all of them will take
+// at least min_duration and at least 1 execution will take less than
+// max_duration.
+template <typename D, typename F>
+void multiAttemptExpectDurationWithin(size_t num_tries,
+                                      D min_duration,
+                                      D max_duration,
+                                      const F& func) {
+  vector<thread> threads(num_tries);
+  vector<D> durations(num_tries, D::min());
+  for (size_t i = 0; i < num_tries; ++i) {
+    threads[i] = thread([&,i]{
+      auto start = steady_clock::now();
+      func();
+      durations[i] = duration_cast<D>(steady_clock::now() - start);
+    });
+  }
+  for (auto& t : threads) {
+    t.join();
+  }
+  sort(durations.begin(), durations.end());
+  for (auto d : durations) {
+    EXPECT_GE(d, min_duration);
+  }
+  EXPECT_LE(durations[0], max_duration);
+}
+
+TEST(RetryingTest, has_op_call) {
+  using ew = exception_wrapper;
+  auto policy_raw = [](size_t n, const ew&) { return n < 3; };
+  auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); };
+  using namespace futures::detail;
+  EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value);
+  EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value);
+}
+
+TEST(RetryingTest, basic) {
+  auto r = futures::retrying(
+      [](size_t n, const exception_wrapper&) { return n < 3; },
+      [](size_t n) {
+          return n < 2
+            ? makeFuture<size_t>(runtime_error("ha"))
+            : makeFuture(n);
+      }
+  ).wait();
+  EXPECT_EQ(2, r.value());
+}
+
+TEST(RetryingTest, policy_future) {
+  atomic<size_t> sleeps {0};
+  auto r = futures::retrying(
+      [&](size_t n, const exception_wrapper&) {
+          return n < 3
+            ? makeFuture(++sleeps).then([] { return true; })
+            : makeFuture(false);
+      },
+      [](size_t n) {
+          return n < 2
+            ? makeFuture<size_t>(runtime_error("ha"))
+            : makeFuture(n);
+      }
+  ).wait();
+  EXPECT_EQ(2, r.value());
+  EXPECT_EQ(2, sleeps);
+}
+
+TEST(RetryingTest, policy_basic) {
+  auto r = futures::retrying(
+      futures::retryingPolicyBasic(3),
+      [](size_t n) {
+          return n < 2
+            ? makeFuture<size_t>(runtime_error("ha"))
+            : makeFuture(n);
+      }
+  ).wait();
+  EXPECT_EQ(2, r.value());
+}
+
+TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
+  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
+    using ms = milliseconds;
+    auto r = futures::retrying(
+        futures::retryingPolicyCappedJitteredExponentialBackoff(
+          3, ms(100), ms(1000), 0.1, mt19937_64(0),
+          [](size_t, const exception_wrapper&) { return true; }),
+        [](size_t n) {
+            return n < 2
+              ? makeFuture<size_t>(runtime_error("ha"))
+              : makeFuture(n);
+        }
+    ).wait();
+    EXPECT_EQ(2, r.value());
+  });
+}
+
+TEST(RetryingTest, policy_sleep_defaults) {
+  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
+    //  To ensure that this compiles with default params.
+    using ms = milliseconds;
+    auto r = futures::retrying(
+        futures::retryingPolicyCappedJitteredExponentialBackoff(
+          3, ms(100), ms(1000), 0.1),
+        [](size_t n) {
+            return n < 2
+              ? makeFuture<size_t>(runtime_error("ha"))
+              : makeFuture(n);
+        }
+    ).wait();
+    EXPECT_EQ(2, r.value());
+  });
+}
+
+/*
+TEST(RetryingTest, policy_sleep_cancel) {
+  multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{
+    mt19937_64 rng(0);
+    using ms = milliseconds;
+    auto r = futures::retrying(
+        futures::retryingPolicyCappedJitteredExponentialBackoff(
+          5, ms(100), ms(1000), 0.1, rng,
+          [](size_t n, const exception_wrapper&) { return true; }),
+        [](size_t n) {
+            return n < 4
+              ? makeFuture<size_t>(runtime_error("ha"))
+              : makeFuture(n);
+        }
+    );
+    r.cancel();
+    r.wait();
+    EXPECT_EQ(2, r.value());
+  });
+}
+*/
diff --git a/faux-folly/folly/futures/test/SharedPromiseTest.cpp b/faux-folly/folly/futures/test/SharedPromiseTest.cpp
new file mode 100644
index 0000000..495526d
--- /dev/null
+++ b/faux-folly/folly/futures/test/SharedPromiseTest.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/SharedPromise.h>
+
+using namespace folly;
+
+TEST(SharedPromise, setGet) {
+  SharedPromise<int> p;
+  p.setValue(1);
+  auto f1 = p.getFuture();
+  auto f2 = p.getFuture();
+  EXPECT_EQ(1, f1.value());
+  EXPECT_EQ(1, f2.value());
+}
+TEST(SharedPromise, getSet) {
+  SharedPromise<int> p;
+  auto f1 = p.getFuture();
+  auto f2 = p.getFuture();
+  p.setValue(1);
+  EXPECT_EQ(1, f1.value());
+  EXPECT_EQ(1, f2.value());
+}
+
+TEST(SharedPromise, getSetGet) {
+  SharedPromise<int> p;
+  auto f1 = p.getFuture();
+  p.setValue(1);
+  auto f2 = p.getFuture();
+  EXPECT_EQ(1, f1.value());
+  EXPECT_EQ(1, f2.value());
+}
+
+TEST(SharedPromise, reset) {
+  SharedPromise<int> p;
+
+  auto f1 = p.getFuture();
+  p.setValue(1);
+  EXPECT_EQ(1, f1.value());
+
+  p = SharedPromise<int>();
+  auto f2 = p.getFuture();
+  EXPECT_FALSE(f2.isReady());
+  p.setValue(2);
+  EXPECT_EQ(2, f2.value());
+}
+
+TEST(SharedPromise, getMoveSet) {
+  SharedPromise<int> p;
+  auto f = p.getFuture();
+  auto p2 = std::move(p);
+  p2.setValue(1);
+  EXPECT_EQ(1, f.value());
+}
+
+TEST(SharedPromise, setMoveGet) {
+  SharedPromise<int> p;
+  p.setValue(1);
+  auto p2 = std::move(p);
+  auto f = p2.getFuture();
+  EXPECT_EQ(1, f.value());
+}
+
+TEST(SharedPromise, moveSetGet) {
+  SharedPromise<int> p;
+  auto p2 = std::move(p);
+  p2.setValue(1);
+  auto f = p2.getFuture();
+  EXPECT_EQ(1, f.value());
+}
+
+TEST(SharedPromise, moveGetSet) {
+  SharedPromise<int> p;
+  auto p2 = std::move(p);
+  auto f = p2.getFuture();
+  p2.setValue(1);
+  EXPECT_EQ(1, f.value());
+}
+
+TEST(SharedPromise, moveMove) {
+  SharedPromise<std::shared_ptr<int>> p;
+  auto f1 = p.getFuture();
+  auto f2 = p.getFuture();
+  auto p2 = std::move(p);
+  p = std::move(p2);
+  p.setValue(std::make_shared<int>(1));
+}
diff --git a/faux-folly/folly/futures/test/ThenCompileTest.cpp b/faux-folly/folly/futures/test/ThenCompileTest.cpp
new file mode 100644
index 0000000..0678d15
--- /dev/null
+++ b/faux-folly/folly/futures/test/ThenCompileTest.cpp
@@ -0,0 +1,92 @@
+// This file is @generated by then_compile_test.rb. Do not edit directly.
+
+#include <folly/futures/test/ThenCompileTest.h>
+
+using namespace folly;
+
+TEST(Basic, thenVariants) {
+  SomeClass anObject;
+
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, Try<A>&&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>&&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A> const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A> const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, Try<A> const&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A> const&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A> const&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, Try<A>>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, Try<A>&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, A&&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A&&>());}
+  {Future<B> f = someFuture<A>().then([&](A&&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, A const&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A const&>());}
+  {Future<B> f = someFuture<A>().then([&](A const&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, A>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A>());}
+  {Future<B> f = someFuture<A>().then([&](A){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<Future<B>, A&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, A&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<Future<B>, A&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, A&>());}
+  {Future<B> f = someFuture<A>().then([&](A&){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then([&](){return someFuture<B>();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, Try<A>&&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>&&){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A> const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A> const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, Try<A> const&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A> const&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A> const&){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, Try<A>>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, Try<A>&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&>());}
+  {Future<B> f = someFuture<A>().then([&](Try<A>&){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, A&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A&&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, A&&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, A&&>());}
+  {Future<B> f = someFuture<A>().then([&](A&&){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, A const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A const&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, A const&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, A const&>());}
+  {Future<B> f = someFuture<A>().then([&](A const&){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, A>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, A>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, A>());}
+  {Future<B> f = someFuture<A>().then([&](A){return B();});}
+  {Future<B> f = someFuture<A>().then(&aFunction<B, A&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, A&>);}
+  {Future<B> f = someFuture<A>().then(&SomeClass::aMethod<B, A&>, &anObject);}
+  {Future<B> f = someFuture<A>().then(aStdFunction<B, A&>());}
+  {Future<B> f = someFuture<A>().then([&](A&){return B();});}
+  {Future<B> f = someFuture<A>().then([&](){return B();});}
+}
diff --git a/faux-folly/folly/futures/test/ThenCompileTest.h b/faux-folly/folly/futures/test/ThenCompileTest.h
new file mode 100644
index 0000000..c8327e7
--- /dev/null
+++ b/faux-folly/folly/futures/test/ThenCompileTest.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+#include <memory>
+
+namespace folly {
+
+typedef std::unique_ptr<int> A;
+struct B {};
+
+template <class T>
+using EnableIfFuture = typename std::enable_if<isFuture<T>::value>::type;
+
+template <class T>
+using EnableUnlessFuture = typename std::enable_if<!isFuture<T>::value>::type;
+
+template <class T>
+Future<T> someFuture() {
+  return makeFuture(T());
+}
+
+template <class Ret, class... Params>
+typename std::enable_if<isFuture<Ret>::value, Ret>::type
+aFunction(Params...) {
+  typedef typename Ret::value_type T;
+  return makeFuture(T());
+}
+
+template <class Ret, class... Params>
+typename std::enable_if<!isFuture<Ret>::value, Ret>::type
+aFunction(Params...) {
+  return Ret();
+}
+
+template <class Ret, class... Params>
+std::function<Ret(Params...)>
+aStdFunction(
+    typename std::enable_if<!isFuture<Ret>::value, bool>::type = false) {
+  return [](Params...) -> Ret { return Ret(); };
+}
+
+template <class Ret, class... Params>
+std::function<Ret(Params...)>
+aStdFunction(typename std::enable_if<isFuture<Ret>::value, bool>::type = true) {
+  typedef typename Ret::value_type T;
+  return [](Params...) -> Future<T> { return makeFuture(T()); };
+}
+
+class SomeClass {
+public:
+  template <class Ret, class... Params>
+  static
+  typename std::enable_if<!isFuture<Ret>::value, Ret>::type
+  aStaticMethod(Params...) {
+    return Ret();
+  }
+
+  template <class Ret, class... Params>
+  static
+  typename std::enable_if<isFuture<Ret>::value, Ret>::type
+  aStaticMethod(Params...) {
+    typedef typename Ret::value_type T;
+    return makeFuture(T());
+  }
+
+  template <class Ret, class... Params>
+  typename std::enable_if<!isFuture<Ret>::value, Ret>::type
+  aMethod(Params...) {
+    return Ret();
+  }
+
+  template <class Ret, class... Params>
+  typename std::enable_if<isFuture<Ret>::value, Ret>::type
+  aMethod(Params...) {
+    typedef typename Ret::value_type T;
+    return makeFuture(T());
+  }
+};
+
+}
diff --git a/faux-folly/folly/futures/test/ThenTest.cpp b/faux-folly/folly/futures/test/ThenTest.cpp
new file mode 100644
index 0000000..d92dd00
--- /dev/null
+++ b/faux-folly/folly/futures/test/ThenTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+#include <thread>
+
+using namespace folly;
+
+struct Widget {
+  int v_, copied_, moved_;
+  /* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {}
+  Widget(const Widget& other)
+    : v_(other.v_), copied_(other.copied_ + 1), moved_(other.moved_) {}
+  Widget(Widget&& other) noexcept
+    : v_(other.v_), copied_(other.copied_), moved_(other.moved_ + 1) {}
+  Widget& operator=(const Widget& other)
+    { throw std::logic_error("unexpected copy assignment"); }
+  Widget& operator=(Widget&& other)
+    { throw std::logic_error("unexpected move assignment"); }
+};
+
+TEST(Then, tryConstructor) {
+  auto t = Try<Widget>(23);
+  EXPECT_EQ(t.value().v_, 23);
+  EXPECT_EQ(t.value().copied_, 0);
+  EXPECT_EQ(t.value().moved_, 1);
+}
+
+TEST(Then, makeFuture) {
+  auto future = makeFuture<Widget>(23);
+  EXPECT_EQ(future.value().v_, 23);
+  EXPECT_EQ(future.value().copied_, 0);
+  EXPECT_EQ(future.value().moved_, 2);
+}
+
+TEST(Then, tryConstRValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](const Try<Widget>&& t) {
+      EXPECT_EQ(t.value().copied_, 0);
+      EXPECT_EQ(t.value().moved_, 2);
+      return t.value().v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, tryRValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](Try<Widget>&& t) {
+      EXPECT_EQ(t.value().copied_, 0);
+      EXPECT_EQ(t.value().moved_, 2);
+      return t.value().v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, tryLValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](Try<Widget>& t) {
+      EXPECT_EQ(t.value().copied_, 0);
+      EXPECT_EQ(t.value().moved_, 2);
+      return t.value().v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, tryConstLValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](const Try<Widget>& t) {
+      EXPECT_EQ(t.value().copied_, 0);
+      EXPECT_EQ(t.value().moved_, 2);
+      return t.value().v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, tryValue) {
+  auto future = makeFuture<Widget>(23).then(
+    [](Try<Widget> t) {
+      EXPECT_EQ(t.value().copied_, 0);
+      EXPECT_EQ(t.value().moved_, 3);
+      return t.value().v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, tryConstValue) {
+  auto future = makeFuture<Widget>(23).then(
+    [](const Try<Widget> t) {
+      EXPECT_EQ(t.value().copied_, 0);
+      EXPECT_EQ(t.value().moved_, 3);
+      return t.value().v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, constRValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](const Widget&& w) {
+      EXPECT_EQ(w.copied_, 0);
+      EXPECT_EQ(w.moved_, 2);
+      return w.v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, rValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](Widget&& w) {
+      EXPECT_EQ(w.copied_, 0);
+      EXPECT_EQ(w.moved_, 2);
+      return w.v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, lValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](Widget& w) {
+      EXPECT_EQ(w.copied_, 0);
+      EXPECT_EQ(w.moved_, 2);
+      return w.v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, constLValueReference) {
+  auto future = makeFuture<Widget>(23).then(
+    [](const Widget& w) {
+      EXPECT_EQ(w.copied_, 0);
+      EXPECT_EQ(w.moved_, 2);
+      return w.v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, value) {
+  auto future = makeFuture<Widget>(23).then(
+    [](Widget w) {
+      EXPECT_EQ(w.copied_, 0);
+      EXPECT_EQ(w.moved_, 3);
+      return w.v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, constValue) {
+  auto future = makeFuture<Widget>(23).then(
+    [](const Widget w) {
+      EXPECT_EQ(w.copied_, 0);
+      EXPECT_EQ(w.moved_, 3);
+      return w.v_;
+    });
+  EXPECT_EQ(future.value(), 23);
+}
+
+TEST(Then, voidThenShouldPropagateExceptions) {
+  EXPECT_FALSE(makeFuture(42).then().hasException());
+  EXPECT_TRUE(makeFuture<int>(std::runtime_error("err"))
+             .then().hasException());
+}
diff --git a/faux-folly/folly/futures/test/ThreadWheelTimekeeperTest.cpp b/faux-folly/folly/futures/test/ThreadWheelTimekeeperTest.cpp
new file mode 100644
index 0000000..8e9f70b
--- /dev/null
+++ b/faux-folly/folly/futures/test/ThreadWheelTimekeeperTest.cpp
@@ -0,0 +1,36 @@
+#include <gtest/gtest.h>
+#include <folly/futures/detail/ThreadWheelTimekeeper.h>
+#include <chrono>
+
+#include <unistd.h>
+
+using folly::detail::ThreadWheelTimekeeper;
+using std::chrono::milliseconds;
+
+/* If a callback tries to add a timer, it should not deadlock */
+TEST(ThreadWheelTimekeeper, callbackAddsTimer)
+{
+    ThreadWheelTimekeeper tk;
+    milliseconds now(0);
+    unsigned a = 0;
+    unsigned b = 0;
+
+    /* If this doesn't complete in 5 seconds... crash */
+    alarm(5);
+
+    tk.after(now)
+        .then([&] {
+                a = 1;
+                return tk.after(now)
+                    .then([&] {
+                            b = 1;
+                        });
+            })
+        .get();
+
+    /* cancel timeout */
+    alarm(0);
+
+    EXPECT_EQ(1, a);
+    EXPECT_EQ(1, b);
+}
diff --git a/faux-folly/folly/futures/test/ThreadedExecutorTest.cpp b/faux-folly/folly/futures/test/ThreadedExecutorTest.cpp
new file mode 100644
index 0000000..bfc9ec7
--- /dev/null
+++ b/faux-folly/folly/futures/test/ThreadedExecutorTest.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 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/futures/ThreadedExecutor.h>
+#include <folly/futures/Future.h>
+#include <folly/Conv.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace folly;
+using namespace folly::futures;
+
+class ThreadedExecutorTest : public testing::Test {};
+
+TEST_F(ThreadedExecutorTest, example) {
+  ThreadedExecutor x;
+  auto ret = via(&x)
+    .then([&] { return 17; })
+    .then([&](int x) { return to<string>(x); })
+    .wait()
+    .getTry();
+  EXPECT_EQ("17", ret.value());
+}
diff --git a/faux-folly/folly/futures/test/TimekeeperTest.cpp b/faux-folly/folly/futures/test/TimekeeperTest.cpp
new file mode 100644
index 0000000..0948324
--- /dev/null
+++ b/faux-folly/folly/futures/test/TimekeeperTest.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Timekeeper.h>
+
+#include <unistd.h>
+
+using namespace folly;
+using std::chrono::milliseconds;
+
+std::chrono::milliseconds const zero_ms(0);
+std::chrono::milliseconds const one_ms(1);
+std::chrono::milliseconds const awhile(10);
+std::chrono::milliseconds const context_switch(50);
+std::chrono::seconds const too_long(10);
+
+std::chrono::steady_clock::time_point now() {
+  return std::chrono::steady_clock::now();
+}
+
+struct TimekeeperFixture : public testing::Test {
+  TimekeeperFixture() :
+    timeLord_(folly::detail::getTimekeeperSingleton())
+  {}
+
+  Timekeeper* timeLord_;
+};
+
+TEST_F(TimekeeperFixture, after) {
+  auto t1 = now();
+  auto f = timeLord_->after(awhile);
+  EXPECT_FALSE(f.isReady());
+  f.get();
+  auto t2 = now();
+
+  EXPECT_GE(t2 - t1, awhile);
+}
+
+TEST(Timekeeper, futureGet) {
+  Promise<int> p;
+  auto t = std::thread([&]{ p.setValue(42); });
+  EXPECT_EQ(42, p.getFuture().get());
+  t.join();
+}
+
+TEST(Timekeeper, futureGetBeforeTimeout) {
+  Promise<int> p;
+  auto t = std::thread([&]{ p.setValue(42); });
+  // Technically this is a race and if the test server is REALLY overloaded
+  // and it takes more than a second to do that thread it could be flaky. But
+  // I want a low timeout (in human terms) so if this regresses and someone
+  // runs it by hand they're not sitting there forever wondering why it's
+  // blocked, and get a useful error message instead. If it does get flaky,
+  // empirically increase the timeout to the point where it's very improbable.
+  EXPECT_EQ(42, p.getFuture().get(std::chrono::seconds(2)));
+  t.join();
+}
+
+TEST(Timekeeper, futureGetTimeout) {
+  Promise<int> p;
+  EXPECT_THROW(p.getFuture().get(one_ms), folly::TimedOut);
+}
+
+TEST(Timekeeper, futureSleep) {
+  auto t1 = now();
+  futures::sleep(one_ms).get();
+  EXPECT_GE(now() - t1, one_ms);
+}
+
+TEST(Timekeeper, futureDelayed) {
+  auto t1 = now();
+  auto dur = makeFuture()
+    .delayed(one_ms)
+    .then([=]{ return now() - t1; })
+    .get();
+
+  EXPECT_GE(dur, one_ms);
+}
+
+TEST(Timekeeper, futureWithinThrows) {
+  Promise<int> p;
+  auto f = p.getFuture()
+    .within(one_ms)
+    .onError([](TimedOut&) { return -1; });
+
+  EXPECT_EQ(-1, f.get());
+}
+
+TEST(Timekeeper, futureWithinAlreadyComplete) {
+  auto f = makeFuture(42)
+    .within(one_ms)
+    .onError([&](TimedOut&){ return -1; });
+
+  EXPECT_EQ(42, f.get());
+}
+
+TEST(Timekeeper, futureWithinFinishesInTime) {
+  Promise<int> p;
+  auto f = p.getFuture()
+    .within(std::chrono::minutes(1))
+    .onError([&](TimedOut&){ return -1; });
+  p.setValue(42);
+
+  EXPECT_EQ(42, f.get());
+}
+
+TEST(Timekeeper, futureWithinVoidSpecialization) {
+  makeFuture().within(one_ms);
+}
+
+TEST(Timekeeper, futureWithinException) {
+  Promise<Unit> p;
+  auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
+  EXPECT_THROW(f.get(), std::runtime_error);
+}
+
+TEST(Timekeeper, onTimeout) {
+  bool flag = false;
+  auto v = makeFuture(42).delayed(context_switch)
+    .onTimeout(zero_ms, [&]{ flag = true; return -1; })
+    .get();
+  EXPECT_TRUE(flag);
+  EXPECT_EQ(-1, v);
+}
+
+TEST(Timekeeper, onTimeoutReturnsFuture) {
+  bool flag = false;
+  makeFuture(42).delayed(context_switch)
+    .onTimeout(zero_ms, [&]{ flag = true; return makeFuture(-1); })
+    .get();
+  EXPECT_TRUE(flag);
+}
+
+TEST(Timekeeper, onTimeoutVoid) {
+  makeFuture().delayed(one_ms)
+    .onTimeout(zero_ms, [&]{
+     });
+  makeFuture().delayed(one_ms)
+    .onTimeout(zero_ms, [&]{
+       return makeFuture<Unit>(std::runtime_error("expected"));
+     });
+  // just testing compilation here
+}
+
+TEST(Timekeeper, interruptDoesntCrash) {
+  auto f = futures::sleep(too_long);
+  f.cancel();
+}
+
+TEST(Timekeeper, chainedInterruptTest) {
+  bool test = false;
+  auto f = futures::sleep(milliseconds(100)).then([&](){
+    test = true;
+  });
+  f.cancel();
+  f.wait();
+  EXPECT_FALSE(test);
+}
+
+TEST(Timekeeper, executor) {
+  class ExecutorTester : public Executor {
+   public:
+    void add(Func f) override {
+      count++;
+      f();
+    }
+    std::atomic<int> count{0};
+  };
+
+  auto f = makeFuture();
+  ExecutorTester tester;
+  f.via(&tester).within(one_ms).then([&](){}).wait();
+  EXPECT_EQ(2, tester.count);
+}
+
+// TODO(5921764)
+/*
+TEST(Timekeeper, onTimeoutPropagates) {
+  bool flag = false;
+  EXPECT_THROW(
+    makeFuture(42).delayed(one_ms)
+      .onTimeout(zero_ms, [&]{ flag = true; })
+      .get(),
+    TimedOut);
+  EXPECT_TRUE(flag);
+}
+*/
+
+TEST_F(TimekeeperFixture, atBeforeNow) {
+  auto f = timeLord_->at(now() - too_long);
+  EXPECT_TRUE(f.isReady());
+  EXPECT_FALSE(f.hasException());
+}
+
+TEST_F(TimekeeperFixture, howToCastDuration) {
+  // I'm not sure whether this rounds up or down but it's irrelevant for the
+  // purpose of this example.
+  auto f = timeLord_->after(std::chrono::duration_cast<Duration>(
+      std::chrono::nanoseconds(1)));
+}
diff --git a/faux-folly/folly/futures/test/TimerMapTest.cpp b/faux-folly/folly/futures/test/TimerMapTest.cpp
new file mode 100644
index 0000000..7f466e7
--- /dev/null
+++ b/faux-folly/folly/futures/test/TimerMapTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2015 Nest Labs, Inc. All Rights Reserved.
+ *
+ * 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 <gtest/gtest.h>
+
+#include <folly/futures/detail/TimerMap.h>
+
+#include <thread>
+#include <atomic>
+
+//#define ADD_DEBUGGING_OUTPUT
+
+/* The DEBUG(x) macro will include the expression in the compilation
+ * unit if ADD_DEBUGGING_OUTPUT is defined. Otherwise, the expression
+ * is removed by the preprocessor. It is expected that DEBUG(x) has
+ * a semicolon following it: DEBUG(x);
+ */
+#ifdef ADD_DEBUGGING_OUTPUT
+#define DEBUG(x) x
+#else
+#define DEBUG(x) ((void)0)
+#endif
+
+using namespace folly;
+using folly::detail::TimerMap;
+using std::cout;
+using std::endl;
+using std::atomic;
+
+TEST(TimerMap, PleaseCompile)
+{
+    TimerMap tm(std::chrono::milliseconds(20));
+}
+
+TEST(TimerMap, GetTick)
+{
+    {
+        auto tick = std::chrono::milliseconds(20);
+        TimerMap tm(tick);
+        EXPECT_EQ(tick, tm.getTick());
+    }
+
+    {
+        auto tick = std::chrono::milliseconds(67);
+        TimerMap tm(tick);
+        EXPECT_EQ(tick, tm.getTick());
+    }
+}
+
+TEST(TimerMap, OneTimer)
+{
+    TimerMap tm(std::chrono::milliseconds(20));
+    const std::chrono::milliseconds delay(1000);
+    bool to = false;
+
+    EXPECT_TRUE(tm.empty());
+    tm.scheduleTimeout(delay, [&]() {
+            to = true;
+        });
+    EXPECT_FALSE(tm.empty());
+
+    EXPECT_FALSE(to);
+    auto epoch = TimerMap::clock_type::now();
+
+    do {
+        auto n = TimerMap::clock_type::now();
+        auto later = tm.nextTimeout();
+        auto elapsed = n - epoch;
+        auto wait = later - n;
+
+        if (!tm.empty()) {
+            EXPECT_LE(elapsed, delay);
+            EXPECT_LE(wait, tm.getTick());
+        } else {
+            EXPECT_GE(elapsed, delay);
+            EXPECT_LE(wait, tm.getTick());
+        }
+
+        std::this_thread::sleep_for(wait);
+        EXPECT_FALSE(to);
+        tm.invokeTimers();
+    } while (!tm.empty());
+
+
+    EXPECT_TRUE(to);
+    EXPECT_TRUE(tm.empty());
+}
+
+/* Test that >1 timers with the same execution time will all get fired. */
+TEST(TimerMap, IdenticalTimers)
+{
+    TimerMap tm(std::chrono::milliseconds(20));
+    auto now = TimerMap::clock_type::now();
+    auto expire = now + std::chrono::milliseconds(20);
+    bool one = false;
+    bool two = false;
+    bool three = false;
+
+    EXPECT_TRUE(tm.empty());
+    tm.scheduleAlarm(expire, [&]() { one = true; });
+    EXPECT_FALSE(tm.empty());
+    tm.scheduleAlarm(expire, [&]() { two = true; });
+    EXPECT_FALSE(tm.empty());
+    tm.scheduleAlarm(expire, [&]() { three = true; });
+    EXPECT_FALSE(tm.empty());
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(20));
+
+    EXPECT_FALSE(one);
+    EXPECT_FALSE(two);
+    EXPECT_FALSE(three);
+    EXPECT_FALSE(tm.empty());
+    while (!tm.empty()) {
+        tm.invokeTimers();
+    }
+    EXPECT_TRUE(tm.empty());
+    EXPECT_TRUE(one);
+    EXPECT_TRUE(two);
+    EXPECT_TRUE(three);
+}
+
+/* Test that timers come out in proper order */
+TEST(TimerMap, timerSequencing)
+{
+    const std::chrono::milliseconds ten_ms(10);
+    const std::chrono::milliseconds twenty_ms(20);
+    const std::chrono::milliseconds now(0);
+    TimerMap tm(std::chrono::milliseconds(1));
+    atomic<unsigned> ctr(0);
+
+    tm.scheduleTimeout(twenty_ms, [&] {
+            unsigned exp = 2;
+            EXPECT_TRUE(ctr.compare_exchange_strong(exp, 3));
+            EXPECT_EQ(2, exp);
+        });
+    tm.scheduleTimeout(ten_ms, [&] {
+            unsigned exp = 1;
+            EXPECT_TRUE(ctr.compare_exchange_strong(exp, 2));
+            EXPECT_EQ(1, exp);
+        });
+    tm.scheduleTimeout(now, [&] {
+            unsigned exp = 0;
+            EXPECT_TRUE(ctr.compare_exchange_strong(exp, 1));
+            EXPECT_EQ(0, exp);
+        });
+
+    while (!tm.empty()) {
+        tm.invokeTimers();
+    }
+
+    EXPECT_EQ(3, ctr);
+}
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(ra) (sizeof(ra)/sizeof(ra[0]))
+#endif
+
+namespace {
+
+std::ostream& operator<<(std::ostream& os, const std::chrono::steady_clock::time_point& tp)
+{
+    os << tp.time_since_epoch().count();
+    return os;
+}
+
+} /* anonymous namespace */
+
+TEST(TimerMap, SeveralTimers)
+{
+    /* Set high resolution so tests aren't brittle to timings */
+    TimerMap tm(std::chrono::milliseconds(250));
+    struct timeout_data {
+        std::chrono::milliseconds delay;
+        bool value;
+    };
+    using msecs = std::chrono::milliseconds;
+
+    timeout_data timeouts[] = {
+        { msecs(100), false }, // 0: 2nd pass
+        { msecs( 50), false }, // 1: 2nd pass
+        { msecs(700), false }, // 2: 4th pass
+        { msecs(666), false }, // 3: 4th pass
+        { msecs(  0), false }, // 4: 1st pass (immediate)
+        { msecs(300), false }, // 5: 3rd pass
+        { msecs( 10), false }, // 6: 2nd pass
+        { msecs(400), false }, // 7: 3rd pass
+        { msecs(900), false }, // 8: 5th pass
+        { msecs(410), false }, // 9: 3rd pass
+    };
+
+    for (unsigned k = 0 ; k < ARRAY_SIZE(timeouts) ; ++k) {
+        tm.scheduleTimeout(timeouts[k].delay, [k, &timeouts]() {
+                timeouts[k].value = true;
+            });
+    }
+
+    std::set<unsigned> complete;
+
+    // 1st pass: 4
+    {
+        auto next = tm.nextTimeout();
+        std::this_thread::sleep_until(next);
+        auto n = TimerMap::clock_type::now();
+        EXPECT_LT(next, n);
+        tm.invokeTimers();
+
+        complete.insert(4);
+        DEBUG(cout  << "1ST PASS...\n");
+        for (unsigned k = 0 ; k < ARRAY_SIZE(timeouts) ; ++k) {
+            DEBUG(cout << k << "\n");
+            if (complete.find(k) == complete.end()) {
+                EXPECT_FALSE(timeouts[k].value);
+            } else {
+                EXPECT_TRUE(timeouts[k].value);
+            }
+        }
+        DEBUG(cout << endl);
+    }
+
+    // 2nd pass: 0, 1, 6
+    {
+        auto next = tm.nextTimeout();
+        std::this_thread::sleep_until(next);
+        auto n = TimerMap::clock_type::now();
+        DEBUG(cout << "next = " << next << endl);
+        DEBUG(cout << "now  = " << n << endl);
+        EXPECT_LT(next, n);
+        tm.invokeTimers();
+
+        complete.insert(0);
+        complete.insert(1);
+        complete.insert(6);
+        DEBUG(cout << "2ND PASS...\n");
+        for (unsigned k = 0 ; k < ARRAY_SIZE(timeouts) ; ++k) {
+            DEBUG(cout << k << "\n");
+            if (complete.find(k) == complete.end()) {
+                EXPECT_FALSE(timeouts[k].value);
+            } else {
+                EXPECT_TRUE(timeouts[k].value);
+            }
+        }
+        DEBUG(cout << endl);
+    }
+
+    // 3rd pass: 5, 7, 9
+    {
+        auto next = tm.nextTimeout();
+        std::this_thread::sleep_until(next);
+        auto n = TimerMap::clock_type::now();
+        DEBUG(cout << "next = " << next << endl);
+        DEBUG(cout << "now  = " << n << endl);
+        EXPECT_LT(next, n);
+        tm.invokeTimers();
+
+        complete.insert(5);
+        complete.insert(7);
+        complete.insert(9);
+        DEBUG(cout << "3RD PASS...\n");
+        for (unsigned k = 0 ; k < ARRAY_SIZE(timeouts) ; ++k) {
+            DEBUG(cout << k << "\n");
+            if (complete.find(k) == complete.end()) {
+                EXPECT_FALSE(timeouts[k].value);
+            } else {
+                EXPECT_TRUE(timeouts[k].value);
+            }
+        }
+        DEBUG(cout << endl);
+    }
+
+    // 4th pass: 2, 3
+    {
+        auto next = tm.nextTimeout();
+        std::this_thread::sleep_until(next);
+        auto n = TimerMap::clock_type::now();
+        DEBUG(cout << "next = " << next << endl);
+        DEBUG(cout << "now  = " << n << endl);
+        EXPECT_LT(next, n);
+        tm.invokeTimers();
+
+        complete.insert(2);
+        complete.insert(3);
+        DEBUG(cout << "4TH PASS...\n");
+        for (unsigned k = 0 ; k < ARRAY_SIZE(timeouts) ; ++k) {
+            DEBUG(cout << k << "\n");
+            if (complete.find(k) == complete.end()) {
+                EXPECT_FALSE(timeouts[k].value);
+            } else {
+                EXPECT_TRUE(timeouts[k].value);
+            }
+        }
+        DEBUG(cout << endl);
+    }
+
+    // 5th pass: 8
+    {
+        auto next = tm.nextTimeout();
+        std::this_thread::sleep_until(next);
+        auto n = TimerMap::clock_type::now();
+        DEBUG(cout << "next = " << next << endl);
+        DEBUG(cout << "now  = " << n << endl);
+        EXPECT_LT(next, n);
+        tm.invokeTimers();
+
+        complete.insert(8);
+        DEBUG(cout << "5TH PASS...\n");
+        for (unsigned k = 0 ; k < ARRAY_SIZE(timeouts) ; ++k) {
+            DEBUG(cout << k << "\n");
+            if (complete.find(k) == complete.end()) {
+                EXPECT_FALSE(timeouts[k].value);
+            } else {
+                EXPECT_TRUE(timeouts[k].value);
+            }
+        }
+        DEBUG(cout << endl);
+    }
+
+    EXPECT_TRUE(tm.empty());
+}
diff --git a/faux-folly/folly/futures/test/TimesTest.cpp b/faux-folly/folly/futures/test/TimesTest.cpp
new file mode 100644
index 0000000..6f3e9da
--- /dev/null
+++ b/faux-folly/folly/futures/test/TimesTest.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2015 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 <memory>
+#include <mutex>
+#include <queue>
+
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+
+using namespace folly;
+
+inline void popAndFulfillPromise(
+    std::queue<std::shared_ptr<Promise<Unit>>>& ps,
+    std::mutex& ps_mutex) {
+  ps_mutex.lock();
+  auto p = ps.front();
+  ps.pop();
+  ps_mutex.unlock();
+  p->setValue();
+}
+
+inline std::function<Future<Unit>(void)> makeThunk(
+    std::queue<std::shared_ptr<Promise<Unit>>>& ps,
+    int& interrupt,
+    std::mutex& ps_mutex) {
+  return [&]() mutable {
+    auto p = std::make_shared<Promise<Unit>>();
+    p->setInterruptHandler([&](exception_wrapper const& e) {
+      ++interrupt;
+    });
+    ps_mutex.lock();
+    ps.push(p);
+    ps_mutex.unlock();
+
+    return p->getFuture();
+  };
+}
+
+inline std::function<bool(void)> makePred(int& i) {
+  return [&]() {
+    bool res = i < 3;
+    ++i;
+    return res;
+  };
+}
+
+TEST(Times, success) {
+  std::queue<std::shared_ptr<Promise<Unit>>> ps;
+  std::mutex ps_mutex;
+  int interrupt = 0;
+  bool complete = false;
+  bool failure = false;
+
+  auto thunk = makeThunk(ps, interrupt, ps_mutex);
+  auto f = folly::times(3, thunk)
+    .then([&]() mutable { complete = true; })
+    .onError([&] (FutureException& e) { failure = true; });
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_FALSE(complete);
+  EXPECT_FALSE(failure);
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_FALSE(complete);
+  EXPECT_FALSE(failure);
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_TRUE(f.isReady());
+  EXPECT_TRUE(complete);
+  EXPECT_FALSE(failure);
+}
+
+TEST(Times, failure) {
+  std::queue<std::shared_ptr<Promise<Unit>>> ps;
+  std::mutex ps_mutex;
+  int interrupt = 0;
+  bool complete = false;
+  bool failure = false;
+
+  auto thunk = makeThunk(ps, interrupt, ps_mutex);
+  auto f = folly::times(3, thunk)
+    .then([&]() mutable { complete = true; })
+    .onError([&] (FutureException& e) { failure = true; });
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_FALSE(complete);
+  EXPECT_FALSE(failure);
+
+  ps_mutex.lock();
+  auto p2 = ps.front();
+  ps.pop();
+  ps_mutex.unlock();
+  FutureException eggs("eggs");
+  p2->setException(eggs);
+
+  EXPECT_TRUE(f.isReady());
+  EXPECT_FALSE(complete);
+  EXPECT_TRUE(failure);
+}
+
+TEST(Times, interrupt) {
+  std::queue<std::shared_ptr<Promise<Unit>>> ps;
+  std::mutex ps_mutex;
+  int interrupt = 0;
+  bool complete = false;
+  bool failure = false;
+
+  auto thunk = makeThunk(ps, interrupt, ps_mutex);
+  auto f = folly::times(3, thunk)
+    .then([&]() mutable { complete = true; })
+    .onError([&] (FutureException& e) { failure = true; });
+
+  EXPECT_EQ(0, interrupt);
+
+  FutureException eggs("eggs");
+  f.raise(eggs);
+
+  for (int i = 1; i <= 3; ++i) {
+    EXPECT_EQ(1, interrupt);
+    popAndFulfillPromise(ps, ps_mutex);
+  }
+}
diff --git a/faux-folly/folly/futures/test/TryTest.cpp b/faux-folly/folly/futures/test/TryTest.cpp
new file mode 100644
index 0000000..cc111c1
--- /dev/null
+++ b/faux-folly/folly/futures/test/TryTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/Memory.h>
+#include <folly/futures/Try.h>
+
+using namespace folly;
+
+TEST(Try, basic) {
+  class A {
+   public:
+    A(int x) : x_(x) {}
+
+    int x() const {
+      return x_;
+    }
+   private:
+    int x_;
+  };
+
+  A a(5);
+  Try<A> t_a(std::move(a));
+
+  Try<Unit> t_void;
+
+  EXPECT_EQ(5, t_a.value().x());
+}
+
+// Make sure we can copy Trys for copyable types
+TEST(Try, copy) {
+  Try<int> t;
+  auto t2 = t;
+}
+
+// But don't choke on move-only types
+TEST(Try, moveOnly) {
+  Try<std::unique_ptr<int>> t;
+  std::vector<Try<std::unique_ptr<int>>> v;
+  v.reserve(10);
+}
+
+TEST(Try, makeTryWith) {
+  auto func = []() {
+    return folly::make_unique<int>(1);
+  };
+
+  auto result = makeTryWith(func);
+  EXPECT_TRUE(result.hasValue());
+  EXPECT_EQ(*result.value(), 1);
+}
+
+TEST(Try, makeTryWithThrow) {
+  auto func = []() {
+    throw std::runtime_error("Runtime");
+    return folly::make_unique<int>(1);
+  };
+
+  auto result = makeTryWith(func);
+  EXPECT_TRUE(result.hasException<std::runtime_error>());
+}
+
+TEST(Try, makeTryWithVoid) {
+  auto func = []() {
+    return;
+  };
+
+  auto result = makeTryWith(func);
+  EXPECT_TRUE(result.hasValue());
+}
+
+TEST(Try, makeTryWithVoidThrow) {
+  auto func = []() {
+    throw std::runtime_error("Runtime");
+    return;
+  };
+
+  auto result = makeTryWith(func);
+  EXPECT_TRUE(result.hasException<std::runtime_error>());
+}
diff --git a/faux-folly/folly/futures/test/UnitTest.cpp b/faux-folly/folly/futures/test/UnitTest.cpp
new file mode 100644
index 0000000..0151f69
--- /dev/null
+++ b/faux-folly/folly/futures/test/UnitTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+std::runtime_error eggs("eggs");
+
+TEST(Unit, futureDefaultCtor) {
+  Future<Unit>();
+}
+
+TEST(Unit, operatorEq) {
+  EXPECT_TRUE(Unit{} == Unit{});
+}
+
+TEST(Unit, operatorNe) {
+  EXPECT_FALSE(Unit{} != Unit{});
+}
+
+TEST(Unit, voidOrUnit) {
+  EXPECT_TRUE(is_void_or_unit<Unit>::value);
+  EXPECT_TRUE(is_void_or_unit<Unit>::value);
+  EXPECT_FALSE(is_void_or_unit<int>::value);
+}
+
+TEST(Unit, promiseSetValue) {
+  Promise<Unit> p;
+  p.setValue();
+}
+
+TEST(Unit, liftInt) {
+  using Lifted = Unit::Lift<int>;
+  EXPECT_FALSE(Lifted::value);
+  auto v = std::is_same<int, Lifted::type>::value;
+  EXPECT_TRUE(v);
+}
+
+TEST(Unit, liftVoid) {
+  using Lifted = Unit::Lift<Unit>;
+  EXPECT_TRUE(Lifted::value);
+  auto v = std::is_same<Unit, Lifted::type>::value;
+  EXPECT_TRUE(v);
+}
+
+TEST(Unit, dropInt) {
+  using dropped = typename Unit::Drop<int>;
+  EXPECT_FALSE(dropped::value);
+  EXPECT_TRUE((std::is_same<int, dropped::type>::value));
+}
+
+TEST(Unit, dropUnit) {
+  using dropped = typename Unit::Drop<Unit>;
+  EXPECT_TRUE(dropped::value);
+  EXPECT_TRUE((std::is_void<dropped::type>::value));
+}
+
+TEST(Unit, dropVoid) {
+  using dropped = typename Unit::Drop<void>;
+  EXPECT_TRUE(dropped::value);
+  EXPECT_TRUE((std::is_void<dropped::type>::value));
+}
+
+TEST(Unit, futureToUnit) {
+  Future<Unit> fu = makeFuture(42).unit();
+  fu.value();
+  EXPECT_TRUE(makeFuture<int>(eggs).unit().hasException());
+}
+
+TEST(Unit, voidFutureToUnit) {
+  Future<Unit> fu = makeFuture().unit();
+  fu.value();
+  EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
+}
+
+TEST(Unit, unitFutureToUnitIdentity) {
+  Future<Unit> fu = makeFuture(Unit{}).unit();
+  fu.value();
+  EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
+}
+
+TEST(Unit, toUnitWhileInProgress) {
+  Promise<int> p;
+  Future<Unit> fu = p.getFuture().unit();
+  EXPECT_FALSE(fu.isReady());
+  p.setValue(42);
+  EXPECT_TRUE(fu.isReady());
+}
+
+TEST(Unit, makeFutureWith) {
+  int count = 0;
+  Future<Unit> fu = makeFutureWith([&]{ count++; });
+  EXPECT_EQ(1, count);
+}
diff --git a/faux-folly/folly/futures/test/UnwrapTest.cpp b/faux-folly/folly/futures/test/UnwrapTest.cpp
new file mode 100644
index 0000000..686592c
--- /dev/null
+++ b/faux-folly/folly/futures/test/UnwrapTest.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+// A simple scenario for the unwrap call, when the promise was fulfilled
+// before calling to unwrap.
+TEST(Unwrap, simpleScenario) {
+  Future<int> encapsulated_future = makeFuture(5484);
+  Future<Future<int>> future = makeFuture(std::move(encapsulated_future));
+  EXPECT_EQ(5484, future.unwrap().value());
+}
+
+// Makes sure that unwrap() works when chaning Future's commands.
+TEST(Unwrap, chainCommands) {
+  Future<Future<int>> future = makeFuture(makeFuture(5484));
+  auto unwrapped = future.unwrap().then([](int i){ return i; });
+  EXPECT_EQ(5484, unwrapped.value());
+}
+
+// Makes sure that the unwrap call also works when the promise was not yet
+// fulfilled, and that the returned Future<T> becomes ready once the promise
+// is fulfilled.
+TEST(Unwrap, futureNotReady) {
+  Promise<Future<int>> p;
+  Future<Future<int>> future = p.getFuture();
+  Future<int> unwrapped = future.unwrap();
+  // Sanity - should not be ready before the promise is fulfilled.
+  ASSERT_FALSE(unwrapped.isReady());
+  // Fulfill the promise and make sure the unwrapped future is now ready.
+  p.setValue(makeFuture(5484));
+  ASSERT_TRUE(unwrapped.isReady());
+  EXPECT_EQ(5484, unwrapped.value());
+}
diff --git a/faux-folly/folly/futures/test/ViaTest.cpp b/faux-folly/folly/futures/test/ViaTest.cpp
new file mode 100644
index 0000000..b65872c
--- /dev/null
+++ b/faux-folly/folly/futures/test/ViaTest.cpp
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/InlineExecutor.h>
+#include <folly/futures/ManualExecutor.h>
+#include <folly/futures/DrivableExecutor.h>
+#include <folly/Baton.h>
+#include <folly/MPMCQueue.h>
+
+#include <thread>
+#include <unistd.h>
+#include <deque>
+#include <mutex>
+#include <condition_variable>
+
+using namespace folly;
+
+struct ManualWaiter : public DrivableExecutor {
+  explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
+
+  void add(Func f) override {
+    ex->add(f);
+  }
+
+  void drive() override {
+    ex->wait();
+    ex->run();
+  }
+
+  std::shared_ptr<ManualExecutor> ex;
+};
+
+struct ViaFixture : public testing::Test {
+  ViaFixture() :
+    westExecutor(new ManualExecutor),
+    eastExecutor(new ManualExecutor),
+    waiter(new ManualWaiter(westExecutor)),
+    done(false)
+  {
+    t = std::thread([=] {
+        ManualWaiter eastWaiter(eastExecutor);
+        while (!done)
+          eastWaiter.drive();
+      });
+  }
+
+  ~ViaFixture() override {
+    done = true;
+    eastExecutor->add([=]() { });
+    t.join();
+  }
+
+  void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
+    eastExecutor->add([=]() {
+      cob(a + b);
+    });
+  }
+
+  std::shared_ptr<ManualExecutor> westExecutor;
+  std::shared_ptr<ManualExecutor> eastExecutor;
+  std::shared_ptr<ManualWaiter> waiter;
+  InlineExecutor inlineExecutor;
+  std::atomic<bool> done;
+  std::thread t;
+};
+
+TEST(Via, exceptionOnLaunch) {
+  auto future = makeFuture<int>(std::runtime_error("E"));
+  EXPECT_THROW(future.value(), std::runtime_error);
+}
+
+TEST(Via, thenValue) {
+  auto future = makeFuture(std::move(1))
+    .then([](Try<int>&& t) {
+      return t.value() == 1;
+    })
+    ;
+
+  EXPECT_TRUE(future.value());
+}
+
+TEST(Via, thenFuture) {
+  auto future = makeFuture(1)
+    .then([](Try<int>&& t) {
+      return makeFuture(t.value() == 1);
+    });
+  EXPECT_TRUE(future.value());
+}
+
+static Future<std::string> doWorkStatic(Try<std::string>&& t) {
+  return makeFuture(t.value() + ";static");
+}
+
+TEST(Via, thenFunction) {
+  struct Worker {
+    Future<std::string> doWork(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class");
+    }
+    static Future<std::string> doWorkStatic(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = makeFuture(std::string("start"))
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&Worker::doWork, &w)
+    ;
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+TEST_F(ViaFixture, threadHops) {
+  auto westThreadId = std::this_thread::get_id();
+  auto f = via(eastExecutor.get()).then([=](Try<Unit>&& t) {
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return makeFuture<int>(1);
+  }).via(westExecutor.get()
+  ).then([=](Try<int>&& t) {
+    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+    return t.value();
+  });
+  EXPECT_EQ(f.getVia(waiter.get()), 1);
+}
+
+TEST_F(ViaFixture, chainVias) {
+  auto westThreadId = std::this_thread::get_id();
+  auto f = via(eastExecutor.get()).then([=]() {
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return 1;
+  }).then([=](int val) {
+    return makeFuture(val).via(westExecutor.get())
+      .then([=](int val) mutable {
+        EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+        return val + 1;
+      });
+  }).then([=](int val) {
+    // even though ultimately the future that triggers this one executed in
+    // the west thread, this then() inherited the executor from its
+    // predecessor, ie the eastExecutor.
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return val + 1;
+  }).via(westExecutor.get()).then([=](int val) {
+    // go back to west, so we can wait on it
+    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+    return val + 1;
+  });
+
+  EXPECT_EQ(f.getVia(waiter.get()), 4);
+}
+
+TEST_F(ViaFixture, bareViaAssignment) {
+  auto f = via(eastExecutor.get());
+}
+TEST_F(ViaFixture, viaAssignment) {
+  // via()&&
+  auto f = makeFuture().via(eastExecutor.get());
+  // via()&
+  auto f2 = f.via(eastExecutor.get());
+}
+
+TEST(Via, chain1) {
+  EXPECT_EQ(42,
+            makeFuture()
+            .thenMulti([] { return 42; })
+            .get());
+}
+
+TEST(Via, chain3) {
+  int count = 0;
+  auto f = makeFuture().thenMulti(
+      [&]{ count++; return 3.14159; },
+      [&](double) { count++; return std::string("hello"); },
+      [&]{ count++; return makeFuture(42); });
+  EXPECT_EQ(42, f.get());
+  EXPECT_EQ(3, count);
+}
+
+struct PriorityExecutor : public Executor {
+  void add(Func f) override {}
+
+  void addWithPriority(Func f, int8_t priority) override {
+    int mid = getNumPriorities() / 2;
+    int p = priority < 0 ?
+            std::max(0, mid + priority) :
+            std::min(getNumPriorities() - 1, mid + priority);
+    EXPECT_LT(p, 3);
+    EXPECT_GE(p, 0);
+    if (p == 0) {
+      count0++;
+    } else if (p == 1) {
+      count1++;
+    } else if (p == 2) {
+      count2++;
+    }
+    f();
+  }
+
+  uint8_t getNumPriorities() const override {
+    return 3;
+  }
+
+  int count0{0};
+  int count1{0};
+  int count2{0};
+};
+
+TEST(Via, priority) {
+  PriorityExecutor exe;
+  via(&exe, -1).then([]{});
+  via(&exe, 0).then([]{});
+  via(&exe, 1).then([]{});
+  via(&exe, 42).then([]{});  // overflow should go to max priority
+  via(&exe, -42).then([]{}); // underflow should go to min priority
+  via(&exe).then([]{});      // default to mid priority
+  via(&exe, Executor::LO_PRI).then([]{});
+  via(&exe, Executor::HI_PRI).then([]{});
+  EXPECT_EQ(3, exe.count0);
+  EXPECT_EQ(2, exe.count1);
+  EXPECT_EQ(3, exe.count2);
+}
+
+TEST_F(ViaFixture, chainX1) {
+  EXPECT_EQ(42,
+            makeFuture()
+            .thenMultiWithExecutor(eastExecutor.get(),[] { return 42; })
+            .get());
+}
+
+TEST_F(ViaFixture, chainX3) {
+  auto westThreadId = std::this_thread::get_id();
+  int count = 0;
+  auto f = via(westExecutor.get()).thenMultiWithExecutor(
+      eastExecutor.get(),
+      [&]{
+        EXPECT_NE(std::this_thread::get_id(), westThreadId);
+        count++; return 3.14159;
+      },
+      [&](double) { count++; return std::string("hello"); },
+      [&]{ count++; })
+    .then([&](){
+        EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+        return makeFuture(42);
+    });
+  EXPECT_EQ(42, f.getVia(waiter.get()));
+  EXPECT_EQ(3, count);
+}
+
+TEST(Via, then2) {
+  ManualExecutor x1, x2;
+  bool a = false, b = false, c = false;
+  via(&x1)
+    .then([&]{ a = true; })
+    .then(&x2, [&]{ b = true; })
+    .then([&]{ c = true; });
+
+  EXPECT_FALSE(a);
+  EXPECT_FALSE(b);
+
+  x1.run();
+  EXPECT_TRUE(a);
+  EXPECT_FALSE(b);
+  EXPECT_FALSE(c);
+
+  x2.run();
+  EXPECT_TRUE(b);
+  EXPECT_FALSE(c);
+
+  x1.run();
+  EXPECT_TRUE(c);
+}
+
+TEST(Via, then2Variadic) {
+  struct Foo { bool a = false; void foo(Try<Unit>) { a = true; } };
+  Foo f;
+  ManualExecutor x;
+  makeFuture().then(&x, &Foo::foo, &f);
+  EXPECT_FALSE(f.a);
+  x.run();
+  EXPECT_TRUE(f.a);
+}
+
+#ifndef __APPLE__ // TODO #7372389
+/// Simple executor that does work in another thread
+class ThreadExecutor : public Executor {
+  std::mutex mx;
+  std::condition_variable cond;
+  std::deque<Func> funcs2;
+  std::atomic<bool> done {false};
+  folly::Baton<> baton;
+  std::thread worker;
+
+  void work() {
+    baton.post();
+    Func fn;
+    std::unique_lock<std::mutex> lock(mx);
+    while (!done) {
+      while (!funcs2.empty()) {
+        fn = std::move(funcs2.front());
+        funcs2.pop_front();
+        lock.unlock();
+        fn();
+        lock.lock();
+      }
+      if (!done && funcs2.empty())
+          cond.wait(lock);
+    }
+  }
+
+ public:
+  explicit ThreadExecutor(size_t n = 1024)
+  {
+    worker = std::thread(std::bind(&ThreadExecutor::work, this));
+  }
+
+  ~ThreadExecutor() override {
+    {
+        std::lock_guard<std::mutex> lock(mx);
+        done = true;
+        cond.notify_one();
+    }
+    worker.join();
+  }
+
+  void add(Func fn) override {
+      std::lock_guard<std::mutex> lock(mx);
+      funcs2.push_back(std::move(fn));
+      cond.notify_one();
+  }
+
+  void waitForStartup() {
+    baton.wait();
+  }
+};
+
+TEST(Via, viaThenGetWasRacy) {
+  ThreadExecutor x;
+  std::unique_ptr<int> val = folly::via(&x)
+    .then([] {
+            return folly::make_unique<int>(42);
+        })
+    .get();
+  ASSERT_TRUE(!!val);
+  EXPECT_EQ(42, *val);
+}
+
+TEST(Via, callbackRace) {
+  ThreadExecutor x;
+
+  auto fn = [&x]{
+    auto promises = std::make_shared<std::vector<Promise<Unit>>>(4);
+    std::vector<Future<Unit>> futures;
+
+    for (auto& p : *promises) {
+      futures.emplace_back(
+        p.getFuture()
+        .via(&x)
+        .then([](Try<Unit>&&){}));
+    }
+
+    x.waitForStartup();
+    x.add([promises]{
+      for (auto& p : *promises) {
+        p.setValue();
+      }
+    });
+
+    return collectAll(futures);
+  };
+
+  fn().wait();
+}
+#endif
+
+class DummyDrivableExecutor : public DrivableExecutor {
+ public:
+  void add(Func f) override {}
+  void drive() override { ran = true; }
+  bool ran{false};
+};
+
+TEST(Via, getVia) {
+  {
+    // non-void
+    ManualExecutor x;
+    auto f = via(&x).then([]{ return true; });
+    EXPECT_TRUE(f.getVia(&x));
+  }
+
+  {
+    // void
+    ManualExecutor x;
+    auto f = via(&x).then();
+    f.getVia(&x);
+  }
+
+  {
+    DummyDrivableExecutor x;
+    auto f = makeFuture(true);
+    EXPECT_TRUE(f.getVia(&x));
+    EXPECT_FALSE(x.ran);
+  }
+}
+
+TEST(Via, waitVia) {
+  {
+    ManualExecutor x;
+    auto f = via(&x).then();
+    EXPECT_FALSE(f.isReady());
+    f.waitVia(&x);
+    EXPECT_TRUE(f.isReady());
+  }
+
+  {
+    // try rvalue as well
+    ManualExecutor x;
+    auto f = via(&x).then().waitVia(&x);
+    EXPECT_TRUE(f.isReady());
+  }
+
+  {
+    DummyDrivableExecutor x;
+    makeFuture(true).waitVia(&x);
+    EXPECT_FALSE(x.ran);
+  }
+}
+
+TEST(Via, viaRaces) {
+  ManualExecutor x;
+  Promise<Unit> p;
+  auto tid = std::this_thread::get_id();
+  bool done = false;
+
+  std::thread t1([&] {
+    p.getFuture()
+      .via(&x)
+      .then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<Unit>&&) { done = true; });
+  });
+
+  std::thread t2([&] {
+    p.setValue();
+  });
+
+  while (!done) x.run();
+  t1.join();
+  t2.join();
+}
+
+TEST(ViaFunc, liftsVoid) {
+  ManualExecutor x;
+  int count = 0;
+  Future<Unit> f = via(&x, [&]{ count++; });
+
+  EXPECT_EQ(0, count);
+  x.run();
+  EXPECT_EQ(1, count);
+}
+
+TEST(ViaFunc, value) {
+  ManualExecutor x;
+  EXPECT_EQ(42, via(&x, []{ return 42; }).getVia(&x));
+}
+
+TEST(ViaFunc, exception) {
+  ManualExecutor x;
+  EXPECT_THROW(
+    via(&x, []() -> int { throw std::runtime_error("expected"); })
+      .getVia(&x),
+    std::runtime_error);
+}
+
+TEST(ViaFunc, future) {
+  ManualExecutor x;
+  EXPECT_EQ(42, via(&x, []{ return makeFuture(42); })
+            .getVia(&x));
+}
+
+TEST(ViaFunc, voidFuture) {
+  ManualExecutor x;
+  int count = 0;
+  via(&x, [&]{ count++; }).getVia(&x);
+  EXPECT_EQ(1, count);
+}
+
+TEST(ViaFunc, isSticky) {
+  ManualExecutor x;
+  int count = 0;
+
+  auto f = via(&x, [&]{ count++; });
+  x.run();
+
+  f.then([&]{ count++; });
+  EXPECT_EQ(1, count);
+  x.run();
+  EXPECT_EQ(2, count);
+}
diff --git a/faux-folly/folly/futures/test/WaitTest.cpp b/faux-folly/folly/futures/test/WaitTest.cpp
new file mode 100644
index 0000000..233d6cc
--- /dev/null
+++ b/faux-folly/folly/futures/test/WaitTest.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/ManualExecutor.h>
+#include <folly/Baton.h>
+
+using std::vector;
+using std::chrono::milliseconds;
+
+using folly::Future;
+using folly::ManualExecutor;
+using folly::Promise;
+using folly::Try;
+using folly::Unit;
+using folly::collectAll;
+using folly::makeFuture;
+
+TEST(Wait, waitImmediate) {
+  makeFuture().wait();
+  auto done = makeFuture(42).wait().value();
+  EXPECT_EQ(42, done);
+
+  vector<int> v{1,2,3};
+  auto done_v = makeFuture(v).wait().value();
+  EXPECT_EQ(v.size(), done_v.size());
+  EXPECT_EQ(v, done_v);
+
+  vector<Future<Unit>> v_f;
+  v_f.push_back(makeFuture());
+  v_f.push_back(makeFuture());
+  auto done_v_f = collectAll(v_f).wait().value();
+  EXPECT_EQ(2, done_v_f.size());
+
+  vector<Future<bool>> v_fb;
+  v_fb.push_back(makeFuture(true));
+  v_fb.push_back(makeFuture(false));
+  auto fut = collectAll(v_fb);
+  auto done_v_fb = std::move(fut.wait().value());
+  EXPECT_EQ(2, done_v_fb.size());
+}
+
+TEST(Wait, wait) {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  std::atomic<bool> flag{false};
+  std::atomic<int> result{1};
+  std::atomic<std::thread::id> id;
+
+  std::thread t([&](Future<int>&& tf){
+      auto n = tf.then([&](Try<int> && t) {
+          id = std::this_thread::get_id();
+          return t.value();
+        });
+      flag = true;
+      result.store(n.wait().value());
+    },
+    std::move(f)
+    );
+  while(!flag){}
+  EXPECT_EQ(result.load(), 1);
+  p.setValue(42);
+  t.join();
+  // validate that the callback ended up executing in this thread, which
+  // is more to ensure that this test actually tests what it should
+  EXPECT_EQ(id, std::this_thread::get_id());
+  EXPECT_EQ(result.load(), 42);
+}
+
+struct MoveFlag {
+  MoveFlag() = default;
+  MoveFlag& operator=(const MoveFlag&) = delete;
+  MoveFlag(const MoveFlag&) = delete;
+  MoveFlag(MoveFlag&& other) noexcept {
+    other.moved = true;
+  }
+  bool moved{false};
+};
+
+TEST(Wait, waitReplacesSelf) {
+  // wait
+  {
+    // lvalue
+    auto f1 = makeFuture(MoveFlag());
+    f1.wait();
+    EXPECT_FALSE(f1.value().moved);
+
+    // rvalue
+    auto f2 = makeFuture(MoveFlag()).wait();
+    EXPECT_FALSE(f2.value().moved);
+  }
+
+  // wait(Duration)
+  {
+    // lvalue
+    auto f1 = makeFuture(MoveFlag());
+    f1.wait(milliseconds(1));
+    EXPECT_FALSE(f1.value().moved);
+
+    // rvalue
+    auto f2 = makeFuture(MoveFlag()).wait(milliseconds(1));
+    EXPECT_FALSE(f2.value().moved);
+  }
+
+  // waitVia
+  {
+    ManualExecutor me;
+    // lvalue
+    auto f1 = makeFuture(MoveFlag());
+    f1.waitVia(&me);
+    EXPECT_FALSE(f1.value().moved);
+
+    // rvalue
+    auto f2 = makeFuture(MoveFlag()).waitVia(&me);
+    EXPECT_FALSE(f2.value().moved);
+  }
+}
+
+TEST(Wait, waitWithDuration) {
+ {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  f.wait(milliseconds(1));
+  EXPECT_FALSE(f.isReady());
+  p.setValue(1);
+  EXPECT_TRUE(f.isReady());
+ }
+ {
+  Promise<int> p;
+  Future<int> f = p.getFuture();
+  p.setValue(1);
+  f.wait(milliseconds(1));
+  EXPECT_TRUE(f.isReady());
+ }
+ {
+  vector<Future<bool>> v_fb;
+  v_fb.push_back(makeFuture(true));
+  v_fb.push_back(makeFuture(false));
+  auto f = collectAll(v_fb);
+  f.wait(milliseconds(1));
+  EXPECT_TRUE(f.isReady());
+  EXPECT_EQ(2, f.value().size());
+ }
+ {
+  vector<Future<bool>> v_fb;
+  Promise<bool> p1;
+  Promise<bool> p2;
+  v_fb.push_back(p1.getFuture());
+  v_fb.push_back(p2.getFuture());
+  auto f = collectAll(v_fb);
+  f.wait(milliseconds(1));
+  EXPECT_FALSE(f.isReady());
+  p1.setValue(true);
+  EXPECT_FALSE(f.isReady());
+  p2.setValue(true);
+  EXPECT_TRUE(f.isReady());
+ }
+ {
+  auto f = makeFuture().wait(milliseconds(1));
+  EXPECT_TRUE(f.isReady());
+ }
+
+ {
+   Promise<Unit> p;
+   auto start = std::chrono::steady_clock::now();
+   auto f = p.getFuture().wait(milliseconds(100));
+   auto elapsed = std::chrono::steady_clock::now() - start;
+   EXPECT_GE(elapsed, milliseconds(100));
+   EXPECT_FALSE(f.isReady());
+   p.setValue();
+   EXPECT_TRUE(f.isReady());
+ }
+
+ {
+   // Try to trigger the race where the resultant Future is not yet complete
+   // even if we didn't hit the timeout, and make sure we deal with it properly
+   Promise<Unit> p;
+   folly::Baton<> b;
+   auto t = std::thread([&]{
+     b.post();
+     /* sleep override */ std::this_thread::sleep_for(milliseconds(100));
+     p.setValue();
+   });
+   b.wait();
+   auto f = p.getFuture().wait(std::chrono::seconds(3600));
+   EXPECT_TRUE(f.isReady());
+   t.join();
+ }
+}
diff --git a/faux-folly/folly/futures/test/WhenTest.cpp b/faux-folly/folly/futures/test/WhenTest.cpp
new file mode 100644
index 0000000..9f52f69
--- /dev/null
+++ b/faux-folly/folly/futures/test/WhenTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 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 <memory>
+#include <mutex>
+
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+
+using namespace folly;
+
+TEST(When, predicateFalse) {
+  int i = 0;
+  auto thunk = [&] {
+    return makeFuture().then([&] { i += 1; });
+  };
+
+  // false
+  auto f1 = folly::when(false, thunk);
+  f1.wait();
+  EXPECT_EQ(0, i);
+}
+
+TEST(When, predicateTrue) {
+  int i = 0;
+  auto thunk = [&] {
+    return makeFuture().then([&] { i += 1; });
+  };
+
+  // true
+  auto f2 = folly::when(true, thunk);
+  f2.wait();
+  EXPECT_EQ(1, i);
+}
diff --git a/faux-folly/folly/futures/test/WhileDoTest.cpp b/faux-folly/folly/futures/test/WhileDoTest.cpp
new file mode 100644
index 0000000..a34f982
--- /dev/null
+++ b/faux-folly/folly/futures/test/WhileDoTest.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015 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 <memory>
+#include <mutex>
+#include <queue>
+
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
+
+using namespace folly;
+
+inline void popAndFulfillPromise(
+    std::queue<std::shared_ptr<Promise<Unit>>>& ps,
+    std::mutex& ps_mutex) {
+  ps_mutex.lock();
+  auto p = ps.front();
+  ps.pop();
+  ps_mutex.unlock();
+  p->setValue();
+}
+
+inline std::function<Future<Unit>(void)> makeThunk(
+    std::queue<std::shared_ptr<Promise<Unit>>>& ps,
+    int& interrupt,
+    std::mutex& ps_mutex) {
+  return [&]() mutable {
+    auto p = std::make_shared<Promise<Unit>>();
+    p->setInterruptHandler([&](exception_wrapper const& e) {
+      ++interrupt;
+    });
+    ps_mutex.lock();
+    ps.push(p);
+    ps_mutex.unlock();
+
+    return p->getFuture();
+  };
+}
+
+inline std::function<bool(void)> makePred(int& i) {
+  return [&]() {
+    bool res = i < 3;
+    ++i;
+    return res;
+  };
+}
+
+TEST(WhileDo, success) {
+  std::queue<std::shared_ptr<Promise<Unit>>> ps;
+  std::mutex ps_mutex;
+  int i = 0;
+  int interrupt = 0;
+  bool complete = false;
+  bool failure = false;
+
+  auto pred = makePred(i);
+  auto thunk = makeThunk(ps, interrupt, ps_mutex);
+  auto f = folly::whileDo(pred, thunk)
+    .then([&]() mutable { complete = true; })
+    .onError([&] (FutureException& e) { failure = true; });
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_FALSE(complete);
+  EXPECT_FALSE(failure);
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_FALSE(complete);
+  EXPECT_FALSE(failure);
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_TRUE(f.isReady());
+  EXPECT_TRUE(complete);
+  EXPECT_FALSE(failure);
+}
+
+TEST(WhileDo, failure) {
+  std::queue<std::shared_ptr<Promise<Unit>>> ps;
+  std::mutex ps_mutex;
+  int i = 0;
+  int interrupt = 0;
+  bool complete = false;
+  bool failure = false;
+
+  auto pred = makePred(i);
+  auto thunk = makeThunk(ps, interrupt, ps_mutex);
+  auto f = folly::whileDo(pred, thunk)
+    .then([&]() mutable { complete = true; })
+    .onError([&] (FutureException& e) { failure = true; });
+
+  popAndFulfillPromise(ps, ps_mutex);
+  EXPECT_FALSE(complete);
+  EXPECT_FALSE(failure);
+
+  ps_mutex.lock();
+  auto p2 = ps.front();
+  ps.pop();
+  ps_mutex.unlock();
+  FutureException eggs("eggs");
+  p2->setException(eggs);
+
+  EXPECT_TRUE(f.isReady());
+  EXPECT_FALSE(complete);
+  EXPECT_TRUE(failure);
+}
+
+TEST(WhileDo, interrupt) {
+  std::queue<std::shared_ptr<Promise<Unit>>> ps;
+  std::mutex ps_mutex;
+  int i = 0;
+  int interrupt = 0;
+  bool complete = false;
+  bool failure = false;
+
+  auto pred = makePred(i);
+  auto thunk = makeThunk(ps, interrupt, ps_mutex);
+  auto f = folly::whileDo(pred, thunk)
+    .then([&]() mutable { complete = true; })
+    .onError([&] (FutureException& e) { failure = true; });
+
+  EXPECT_EQ(0, interrupt);
+
+  FutureException eggs("eggs");
+  f.raise(eggs);
+
+  for (int i = 1; i <= 3; ++i) {
+    EXPECT_EQ(1, interrupt);
+    popAndFulfillPromise(ps, ps_mutex);
+  }
+}
diff --git a/faux-folly/folly/futures/test/WillEqualTest.cpp b/faux-folly/folly/futures/test/WillEqualTest.cpp
new file mode 100644
index 0000000..f517c7d
--- /dev/null
+++ b/faux-folly/folly/futures/test/WillEqualTest.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/futures/Future.h>
+
+using namespace folly;
+
+TEST(WillEqual, basic) {
+  // both p1 and p2 already fulfilled
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    p1.setValue(27);
+    p2.setValue(27);
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    EXPECT_TRUE(f1.willEqual(f2).get());
+  }
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    p1.setValue(27);
+    p2.setValue(36);
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    EXPECT_FALSE(f1.willEqual(f2).get());
+  }
+  // both p1 and p2 not yet fulfilled
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    auto f3 = f1.willEqual(f2);
+    p1.setValue(27);
+    p2.setValue(27);
+    EXPECT_TRUE(f3.get());
+  }
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    auto f3 = f1.willEqual(f2);
+    p1.setValue(27);
+    p2.setValue(36);
+    EXPECT_FALSE(f3.get());
+  }
+  // p1 already fulfilled, p2 not yet fulfilled
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    p1.setValue(27);
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    auto f3 = f1.willEqual(f2);
+    p2.setValue(27);
+    EXPECT_TRUE(f3.get());
+  }
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    p1.setValue(27);
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    auto f3 = f1.willEqual(f2);
+    p2.setValue(36);
+    EXPECT_FALSE(f3.get());
+  }
+  // p2 already fulfilled, p1 not yet fulfilled
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    p2.setValue(27);
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    auto f3 = f1.willEqual(f2);
+    p1.setValue(27);
+    EXPECT_TRUE(f3.get());
+  }
+  {
+    Promise<int> p1;
+    Promise<int> p2;
+    p2.setValue(36);
+    auto f1 = p1.getFuture();
+    auto f2 = p2.getFuture();
+    auto f3 = f1.willEqual(f2);
+    p1.setValue(27);
+    EXPECT_FALSE(f3.get());
+  }
+}
diff --git a/faux-folly/folly/futures/test/WindowTest.cpp b/faux-folly/folly/futures/test/WindowTest.cpp
new file mode 100644
index 0000000..86ea735
--- /dev/null
+++ b/faux-folly/folly/futures/test/WindowTest.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <boost/thread/barrier.hpp>
+
+#include <folly/futures/Future.h>
+
+#include <vector>
+
+using namespace folly;
+
+typedef FutureException eggs_t;
+static eggs_t eggs("eggs");
+
+TEST(Window, basic) {
+  // int -> Future<int>
+  auto fn = [](std::vector<int> input, size_t window_size, size_t expect) {
+    auto res = reduce(
+      window(
+        input,
+        [](int i) { return makeFuture(i); },
+        window_size),
+      0,
+      [](int sum, const Try<int>& b) {
+        return sum + *b;
+      }).get();
+    EXPECT_EQ(expect, res);
+  };
+  {
+    // 2 in-flight at a time
+    std::vector<int> input = {1, 2, 3};
+    fn(input, 2, 6);
+  }
+  {
+    // 4 in-flight at a time
+    std::vector<int> input = {1, 2, 3};
+    fn(input, 4, 6);
+  }
+  {
+    // empty input
+    std::vector<int> input;
+    fn(input, 1, 0);
+  }
+  {
+    // int -> Future<Unit>
+    auto res = reduce(
+      window(
+        std::vector<int>({1, 2, 3}),
+        [](int i) { return makeFuture(); },
+        2),
+      0,
+      [](int sum, const Try<Unit>& b) {
+        EXPECT_TRUE(b.hasValue());
+        return sum + 1;
+      }).get();
+    EXPECT_EQ(3, res);
+  }
+  {
+    // string -> return Future<int>
+    auto res = reduce(
+      window(
+        std::vector<std::string>{"1", "2", "3"},
+        [](std::string s) { return makeFuture<int>(folly::to<int>(s)); },
+        2),
+      0,
+      [](int sum, const Try<int>& b) {
+        return sum + *b;
+      }).get();
+    EXPECT_EQ(6, res);
+  }
+}
+
+TEST(Window, parallel) {
+  std::vector<int> input;
+  std::vector<Promise<int>> ps(10);
+  for (size_t i = 0; i < ps.size(); i++) {
+    input.emplace_back(i);
+  }
+  auto f = collect(window(input, [&](int i) {
+    return ps[i].getFuture();
+  }, 3));
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      ps[i].setValue(i);
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  for (size_t i = 0; i < ps.size(); i++) {
+    EXPECT_EQ(i, f.value()[i]);
+  }
+}
+
+TEST(Window, parallelWithError) {
+  std::vector<int> input;
+  std::vector<Promise<int>> ps(10);
+  for (size_t i = 0; i < ps.size(); i++) {
+    input.emplace_back(i);
+  }
+  auto f = collect(window(input, [&](int i) {
+    return ps[i].getFuture();
+  }, 3));
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      if (i == (ps.size()/2)) {
+        ps[i].setException(eggs);
+      } else {
+        ps[i].setValue(i);
+      }
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(Window, allParallelWithError) {
+  std::vector<int> input;
+  std::vector<Promise<int>> ps(10);
+  for (size_t i = 0; i < ps.size(); i++) {
+    input.emplace_back(i);
+  }
+  auto f = collectAll(window(input, [&](int i) {
+    return ps[i].getFuture();
+  }, 3));
+
+  std::vector<std::thread> ts;
+  boost::barrier barrier(ps.size() + 1);
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts.emplace_back([&ps, &barrier, i]() {
+      barrier.wait();
+      if (i == (ps.size()/2)) {
+        ps[i].setException(eggs);
+      } else {
+        ps[i].setValue(i);
+      }
+    });
+  }
+
+  barrier.wait();
+
+  for (size_t i = 0; i < ps.size(); i++) {
+    ts[i].join();
+  }
+
+  EXPECT_TRUE(f.isReady());
+  for (size_t i = 0; i < ps.size(); i++) {
+    if (i == (ps.size()/2)) {
+      EXPECT_THROW(f.value()[i].value(), eggs_t);
+    } else {
+      EXPECT_TRUE(f.value()[i].hasValue());
+      EXPECT_EQ(i, f.value()[i].value());
+    }
+  }
+}
diff --git a/faux-folly/folly/futures/test/then_compile_test.rb b/faux-folly/folly/futures/test/then_compile_test.rb
new file mode 100755
index 0000000..436e6ad
--- /dev/null
+++ b/faux-folly/folly/futures/test/then_compile_test.rb
@@ -0,0 +1,76 @@
+#!/usr/bin/env ruby
+
+# cd folly/futures/test && ruby then_compile_test.rb > ThenCompileTest.cpp
+
+# An exercise in combinatorics.
+# (ordinary/static function, member function, std::function, lambda)
+# X
+# returns (Future<R>, R)
+# X
+# accepts (Try<T>&&, Try<T> const&, Try<T>, T&&, T const&, T, nothing)
+
+def test(*args)
+  args = args.join(", ")
+  [
+  "{Future<B> f = someFuture<A>().then(#{args});}",
+  #"{Future<B> f = makeFuture(A()).then(#{args}, anExecutor);}",
+  ]
+end
+
+def retval(ret)
+  {
+    "Future<B>" => "someFuture<B>()",
+    "Try<B>" => "Try<B>(B())",
+    "B" => "B()"
+  }[ret]
+end
+
+return_types = [
+  "Future<B>",
+  "B",
+  #"Try<B>",
+]
+param_types = [
+    "Try<A>&&",
+    "Try<A> const&",
+    "Try<A>",
+    "Try<A>&",
+    "A&&",
+    "A const&",
+    "A",
+    "A&",
+    "",
+  ]
+
+tests = (
+  return_types.map { |ret|
+    param_types.map { |param|
+      if param != "" then
+        both = "#{ret}, #{param}"
+        [
+          ["&aFunction<#{both}>"],
+          ["&SomeClass::aStaticMethod<#{both}>"],
+          ["&SomeClass::aMethod<#{both}>", "&anObject"],
+          ["aStdFunction<#{both}>()"],
+          ["[&](#{param}){return #{retval(ret)};}"],
+        ]
+      else
+        [["[&](){return #{retval(ret)};}"]]
+      end
+    }
+  }.flatten(2)
+).map {|a| test(a)}.flatten
+
+print <<EOF
+// This file is #{"@"}generated by then_compile_test.rb. Do not edit directly.
+
+#include <folly/futures/test/ThenCompileTest.h>
+
+using namespace folly;
+
+TEST(Basic, thenVariants) {
+  SomeClass anObject;
+
+  #{tests.join("\n  ")}
+}
+EOF
diff --git a/faux-folly/folly/io/Cursor.h b/faux-folly/folly/io/Cursor.h
new file mode 100644
index 0000000..2aa4227
--- /dev/null
+++ b/faux-folly/folly/io/Cursor.h
@@ -0,0 +1,892 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_CURSOR_H
+#define FOLLY_CURSOR_H
+
+#include <assert.h>
+#include <cstdarg>
+#include <stdexcept>
+#include <string.h>
+#include <type_traits>
+#include <memory>
+
+#include <folly/Bits.h>
+#include <folly/io/IOBuf.h>
+#include <folly/io/IOBufQueue.h>
+#include <folly/Likely.h>
+#include <folly/Memory.h>
+#include <folly/Portability.h>
+#include <folly/Range.h>
+
+/**
+ * Cursor class for fast iteration over IOBuf chains.
+ *
+ * Cursor - Read-only access
+ *
+ * RWPrivateCursor - Read-write access, assumes private access to IOBuf chain
+ * RWUnshareCursor - Read-write access, calls unshare on write (COW)
+ * Appender        - Write access, assumes private access to IOBuf chian
+ *
+ * Note that RW cursors write in the preallocated part of buffers (that is,
+ * between the buffer's data() and tail()), while Appenders append to the end
+ * of the buffer (between the buffer's tail() and bufferEnd()).  Appenders
+ * automatically adjust the buffer pointers, so you may only use one
+ * Appender with a buffer chain; for this reason, Appenders assume private
+ * access to the buffer (you need to call unshare() yourself if necessary).
+ **/
+namespace folly { namespace io {
+
+namespace detail {
+
+template <class Derived, class BufType>
+class CursorBase {
+  // Make all the templated classes friends for copy constructor.
+  template <class D, typename B> friend class CursorBase;
+ public:
+  explicit CursorBase(BufType* buf) : crtBuf_(buf), buffer_(buf) { }
+
+  /**
+   * Copy constructor.
+   *
+   * This also allows constructing a CursorBase from other derived types.
+   * For instance, this allows constructing a Cursor from an RWPrivateCursor.
+   */
+  template <class OtherDerived, class OtherBuf>
+  explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor)
+    : crtBuf_(cursor.crtBuf_),
+      offset_(cursor.offset_),
+      buffer_(cursor.buffer_) { }
+
+  /**
+   * Reset cursor to point to a new buffer.
+   */
+  void reset(BufType* buf) {
+    crtBuf_ = buf;
+    buffer_ = buf;
+    offset_ = 0;
+  }
+
+  const uint8_t* data() const {
+    return crtBuf_->data() + offset_;
+  }
+
+  /**
+   * Return the remaining space available in the current IOBuf.
+   *
+   * May return 0 if the cursor is at the end of an IOBuf.  Use peek() instead
+   * if you want to avoid this.  peek() will advance to the next non-empty
+   * IOBuf (up to the end of the chain) if the cursor is currently pointing at
+   * the end of a buffer.
+   */
+  size_t length() const {
+    return crtBuf_->length() - offset_;
+  }
+
+  /**
+   * Return the space available until the end of the entire IOBuf chain.
+   */
+  size_t totalLength() const {
+    if (crtBuf_ == buffer_) {
+      return crtBuf_->computeChainDataLength() - offset_;
+    }
+    CursorBase end(buffer_->prev());
+    end.offset_ = end.buffer_->length();
+    return end - *this;
+  }
+
+  /*
+   * Return true if the cursor is at the end of the entire IOBuf chain.
+   */
+  bool isAtEnd() const {
+    // Check for the simple cases first.
+    if (offset_ != crtBuf_->length()) {
+      return false;
+    }
+    if (crtBuf_ == buffer_->prev()) {
+      return true;
+    }
+    // We are at the end of a buffer, but it isn't the last buffer.
+    // We might still be at the end if the remaining buffers in the chain are
+    // empty.
+    const IOBuf* buf = crtBuf_->next();;
+    while (buf != buffer_) {
+      if (buf->length() > 0) {
+        return false;
+      }
+      buf = buf->next();
+    }
+    return true;
+  }
+
+  Derived& operator+=(size_t offset) {
+    Derived* p = static_cast<Derived*>(this);
+    p->skip(offset);
+    return *p;
+  }
+  Derived operator+(size_t offset) const {
+    Derived other(*this);
+    other.skip(offset);
+    return other;
+  }
+
+  /**
+   * Compare cursors for equality/inequality.
+   *
+   * Two cursors are equal if they are pointing to the same location in the
+   * same IOBuf chain.
+   */
+  bool operator==(const Derived& other) const {
+    return (offset_ == other.offset_) && (crtBuf_ == other.crtBuf_);
+  }
+  bool operator!=(const Derived& other) const {
+    return !operator==(other);
+  }
+
+  template <class T>
+  typename std::enable_if<std::is_arithmetic<T>::value, T>::type read() {
+    T val;
+    if (LIKELY(length() >= sizeof(T))) {
+      val = loadUnaligned<T>(data());
+      offset_ += sizeof(T);
+    } else {
+      pullSlow(&val, sizeof(T));
+    }
+    return val;
+  }
+
+  template <class T>
+  T readBE() {
+    return Endian::big(read<T>());
+  }
+
+  template <class T>
+  T readLE() {
+    return Endian::little(read<T>());
+  }
+
+  /**
+   * Read a fixed-length string.
+   *
+   * The std::string-based APIs should probably be avoided unless you
+   * ultimately want the data to live in an std::string. You're better off
+   * using the pull() APIs to copy into a raw buffer otherwise.
+   */
+  std::string readFixedString(size_t len) {
+    std::string str;
+    str.reserve(len);
+    if (LIKELY(length() >= len)) {
+      str.append(reinterpret_cast<const char*>(data()), len);
+      offset_ += len;
+    } else {
+      readFixedStringSlow(&str, len);
+    }
+    return str;
+  }
+
+  /**
+   * Read a string consisting of bytes until the given terminator character is
+   * seen. Raises an std::length_error if maxLength bytes have been processed
+   * before the terminator is seen.
+   *
+   * See comments in readFixedString() about when it's appropriate to use this
+   * vs. using pull().
+   */
+  std::string readTerminatedString(
+      char termChar = '\0',
+      size_t maxLength = std::numeric_limits<size_t>::max()) {
+    std::string str;
+
+    for (;;) {
+      const uint8_t* buf = data();
+      size_t buflen = length();
+
+      size_t i = 0;
+      while (i < buflen && buf[i] != termChar) {
+        ++i;
+
+        // Do this check after incrementing 'i', as even though we start at the
+        // 0 byte, it still represents a single character
+        if (str.length() + i >= maxLength) {
+          throw std::length_error("string overflow");
+        }
+      }
+
+      str.append(reinterpret_cast<const char*>(buf), i);
+      if (i < buflen) {
+        skip(i + 1);
+        return str;
+      }
+
+      skip(i);
+
+      if (UNLIKELY(!tryAdvanceBuffer())) {
+        throw std::out_of_range("string underflow");
+      }
+    }
+  }
+
+  size_t skipAtMost(size_t len) {
+    if (LIKELY(length() >= len)) {
+      offset_ += len;
+      return len;
+    }
+    return skipAtMostSlow(len);
+  }
+
+  void skip(size_t len) {
+    if (LIKELY(length() >= len)) {
+      offset_ += len;
+    } else {
+      skipSlow(len);
+    }
+  }
+
+  size_t pullAtMost(void* buf, size_t len) {
+    // Fast path: it all fits in one buffer.
+    if (LIKELY(length() >= len)) {
+      memcpy(buf, data(), len);
+      offset_ += len;
+      return len;
+    }
+    return pullAtMostSlow(buf, len);
+  }
+
+  void pull(void* buf, size_t len) {
+    if (LIKELY(length() >= len)) {
+      memcpy(buf, data(), len);
+      offset_ += len;
+    } else {
+      pullSlow(buf, len);
+    }
+  }
+
+  /**
+   * Return the available data in the current buffer.
+   * If you want to gather more data from the chain into a contiguous region
+   * (for hopefully zero-copy access), use gather() before peek().
+   */
+  std::pair<const uint8_t*, size_t> peek() {
+    // Ensure that we're pointing to valid data
+    size_t available = length();
+    while (UNLIKELY(available == 0 && tryAdvanceBuffer())) {
+      available = length();
+    }
+    return std::make_pair(data(), available);
+  }
+
+  void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
+    if (UNLIKELY(cloneAtMost(buf, len) != len)) {
+      throw std::out_of_range("underflow");
+    }
+  }
+
+  void clone(folly::IOBuf& buf, size_t len) {
+    if (UNLIKELY(cloneAtMost(buf, len) != len)) {
+      throw std::out_of_range("underflow");
+    }
+  }
+
+  size_t cloneAtMost(folly::IOBuf& buf, size_t len) {
+    buf = folly::IOBuf();
+
+    std::unique_ptr<folly::IOBuf> tmp;
+    size_t copied = 0;
+    for (int loopCount = 0; true; ++loopCount) {
+      // Fast path: it all fits in one buffer.
+      size_t available = length();
+      if (LIKELY(available >= len)) {
+        if (loopCount == 0) {
+          crtBuf_->cloneOneInto(buf);
+          buf.trimStart(offset_);
+          buf.trimEnd(buf.length() - len);
+        } else {
+          tmp = crtBuf_->cloneOne();
+          tmp->trimStart(offset_);
+          tmp->trimEnd(tmp->length() - len);
+          buf.prependChain(std::move(tmp));
+        }
+
+        offset_ += len;
+        return copied + len;
+      }
+
+      if (loopCount == 0) {
+        crtBuf_->cloneOneInto(buf);
+        buf.trimStart(offset_);
+      } else {
+        tmp = crtBuf_->cloneOne();
+        tmp->trimStart(offset_);
+        buf.prependChain(std::move(tmp));
+      }
+
+      copied += available;
+      if (UNLIKELY(!tryAdvanceBuffer())) {
+        return copied;
+      }
+      len -= available;
+    }
+  }
+
+  size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {
+    if (!buf) {
+      buf = make_unique<folly::IOBuf>();
+    }
+    return cloneAtMost(*buf, len);
+  }
+
+  /**
+   * Return the distance between two cursors.
+   */
+  size_t operator-(const CursorBase& other) const {
+    BufType *otherBuf = other.crtBuf_;
+    size_t len = 0;
+
+    if (otherBuf != crtBuf_) {
+      len += otherBuf->length() - other.offset_;
+
+      for (otherBuf = otherBuf->next();
+           otherBuf != crtBuf_ && otherBuf != other.buffer_;
+           otherBuf = otherBuf->next()) {
+        len += otherBuf->length();
+      }
+
+      if (otherBuf == other.buffer_) {
+        throw std::out_of_range("wrap-around");
+      }
+
+      len += offset_;
+    } else {
+      if (offset_ < other.offset_) {
+        throw std::out_of_range("underflow");
+      }
+
+      len += offset_ - other.offset_;
+    }
+
+    return len;
+  }
+
+  /**
+   * Return the distance from the given IOBuf to the this cursor.
+   */
+  size_t operator-(const BufType* buf) const {
+    size_t len = 0;
+
+    BufType *curBuf = buf;
+    while (curBuf != crtBuf_) {
+      len += curBuf->length();
+      curBuf = curBuf->next();
+      if (curBuf == buf || curBuf == buffer_) {
+        throw std::out_of_range("wrap-around");
+      }
+    }
+
+    len += offset_;
+    return len;
+  }
+
+ protected:
+  ~CursorBase() { }
+
+  BufType* head() {
+    return buffer_;
+  }
+
+  bool tryAdvanceBuffer() {
+    BufType* nextBuf = crtBuf_->next();
+    if (UNLIKELY(nextBuf == buffer_)) {
+      offset_ = crtBuf_->length();
+      return false;
+    }
+
+    offset_ = 0;
+    crtBuf_ = nextBuf;
+    static_cast<Derived*>(this)->advanceDone();
+    return true;
+  }
+
+  BufType* crtBuf_;
+  size_t offset_ = 0;
+
+ private:
+  void readFixedStringSlow(std::string* str, size_t len) {
+    for (size_t available; (available = length()) < len; ) {
+      str->append(reinterpret_cast<const char*>(data()), available);
+      if (UNLIKELY(!tryAdvanceBuffer())) {
+        throw std::out_of_range("string underflow");
+      }
+      len -= available;
+    }
+    str->append(reinterpret_cast<const char*>(data()), len);
+    offset_ += len;
+  }
+
+  size_t pullAtMostSlow(void* buf, size_t len) {
+    uint8_t* p = reinterpret_cast<uint8_t*>(buf);
+    size_t copied = 0;
+    for (size_t available; (available = length()) < len; ) {
+      memcpy(p, data(), available);
+      copied += available;
+      if (UNLIKELY(!tryAdvanceBuffer())) {
+        return copied;
+      }
+      p += available;
+      len -= available;
+    }
+    memcpy(p, data(), len);
+    offset_ += len;
+    return copied + len;
+  }
+
+  void pullSlow(void* buf, size_t len) {
+    if (UNLIKELY(pullAtMostSlow(buf, len) != len)) {
+      throw std::out_of_range("underflow");
+    }
+  }
+
+  size_t skipAtMostSlow(size_t len) {
+    size_t skipped = 0;
+    for (size_t available; (available = length()) < len; ) {
+      skipped += available;
+      if (UNLIKELY(!tryAdvanceBuffer())) {
+        return skipped;
+      }
+      len -= available;
+    }
+    offset_ += len;
+    return skipped + len;
+  }
+
+  void skipSlow(size_t len) {
+    if (UNLIKELY(skipAtMostSlow(len) != len)) {
+      throw std::out_of_range("underflow");
+    }
+  }
+
+  void advanceDone() {
+  }
+
+  BufType* buffer_;
+};
+
+}  // namespace detail
+
+class Cursor : public detail::CursorBase<Cursor, const IOBuf> {
+ public:
+  explicit Cursor(const IOBuf* buf)
+    : detail::CursorBase<Cursor, const IOBuf>(buf) {}
+
+  template <class OtherDerived, class OtherBuf>
+  explicit Cursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
+    : detail::CursorBase<Cursor, const IOBuf>(cursor) {}
+};
+
+namespace detail {
+
+template <class Derived>
+class Writable {
+ public:
+  template <class T>
+  typename std::enable_if<std::is_arithmetic<T>::value>::type
+  write(T value) {
+    const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);
+    Derived* d = static_cast<Derived*>(this);
+    d->push(u8, sizeof(T));
+  }
+
+  template <class T>
+  void writeBE(T value) {
+    Derived* d = static_cast<Derived*>(this);
+    d->write(Endian::big(value));
+  }
+
+  template <class T>
+  void writeLE(T value) {
+    Derived* d = static_cast<Derived*>(this);
+    d->write(Endian::little(value));
+  }
+
+  void push(const uint8_t* buf, size_t len) {
+    Derived* d = static_cast<Derived*>(this);
+    if (d->pushAtMost(buf, len) != len) {
+      throw std::out_of_range("overflow");
+    }
+  }
+
+  void push(ByteRange buf) {
+    if (this->pushAtMost(buf) != buf.size()) {
+      throw std::out_of_range("overflow");
+    }
+  }
+
+  size_t pushAtMost(ByteRange buf) {
+    Derived* d = static_cast<Derived*>(this);
+    return d->pushAtMost(buf.data(), buf.size());
+  }
+
+  /**
+   * push len bytes of data from input cursor, data could be in an IOBuf chain.
+   * If input cursor contains less than len bytes, or this cursor has less than
+   * len bytes writable space, an out_of_range exception will be thrown.
+   */
+  void push(Cursor cursor, size_t len) {
+    if (this->pushAtMost(cursor, len) != len) {
+      throw std::out_of_range("overflow");
+    }
+  }
+
+  size_t pushAtMost(Cursor cursor, size_t len) {
+    size_t written = 0;
+    for(;;) {
+      auto currentBuffer = cursor.peek();
+      const uint8_t* crtData = currentBuffer.first;
+      size_t available = currentBuffer.second;
+      if (available == 0) {
+        // end of buffer chain
+        return written;
+      }
+      // all data is in current buffer
+      if (available >= len) {
+        this->push(crtData, len);
+        cursor.skip(len);
+        return written + len;
+      }
+
+      // write the whole current IOBuf
+      this->push(crtData, available);
+      cursor.skip(available);
+      written += available;
+      len -= available;
+    }
+  }
+};
+
+} // namespace detail
+
+enum class CursorAccess {
+  PRIVATE,
+  UNSHARE
+};
+
+template <CursorAccess access>
+class RWCursor
+  : public detail::CursorBase<RWCursor<access>, IOBuf>,
+    public detail::Writable<RWCursor<access>> {
+  friend class detail::CursorBase<RWCursor<access>, IOBuf>;
+ public:
+  explicit RWCursor(IOBuf* buf)
+    : detail::CursorBase<RWCursor<access>, IOBuf>(buf),
+      maybeShared_(true) {}
+
+  template <class OtherDerived, class OtherBuf>
+  explicit RWCursor(const detail::CursorBase<OtherDerived, OtherBuf>& cursor)
+    : detail::CursorBase<RWCursor<access>, IOBuf>(cursor),
+      maybeShared_(true) {}
+  /**
+   * Gather at least n bytes contiguously into the current buffer,
+   * by coalescing subsequent buffers from the chain as necessary.
+   */
+  void gather(size_t n) {
+    // Forbid attempts to gather beyond the end of this IOBuf chain.
+    // Otherwise we could try to coalesce the head of the chain and end up
+    // accidentally freeing it, invalidating the pointer owned by external
+    // code.
+    //
+    // If crtBuf_ == head() then IOBuf::gather() will perform all necessary
+    // checking.  We only have to perform an explicit check here when calling
+    // gather() on a non-head element.
+    if (this->crtBuf_ != this->head() && this->totalLength() < n) {
+      throw std::overflow_error("cannot gather() past the end of the chain");
+    }
+    this->crtBuf_->gather(this->offset_ + n);
+  }
+  void gatherAtMost(size_t n) {
+    size_t size = std::min(n, this->totalLength());
+    return this->crtBuf_->gather(this->offset_ + size);
+  }
+
+  using detail::Writable<RWCursor<access>>::pushAtMost;
+  size_t pushAtMost(const uint8_t* buf, size_t len) {
+    size_t copied = 0;
+    for (;;) {
+      // Fast path: the current buffer is big enough.
+      size_t available = this->length();
+      if (LIKELY(available >= len)) {
+        if (access == CursorAccess::UNSHARE) {
+          maybeUnshare();
+        }
+        memcpy(writableData(), buf, len);
+        this->offset_ += len;
+        return copied + len;
+      }
+
+      if (access == CursorAccess::UNSHARE) {
+        maybeUnshare();
+      }
+      memcpy(writableData(), buf, available);
+      copied += available;
+      if (UNLIKELY(!this->tryAdvanceBuffer())) {
+        return copied;
+      }
+      buf += available;
+      len -= available;
+    }
+  }
+
+  void insert(std::unique_ptr<folly::IOBuf> buf) {
+    folly::IOBuf* nextBuf;
+    if (this->offset_ == 0) {
+      // Can just prepend
+      nextBuf = this->crtBuf_;
+      this->crtBuf_->prependChain(std::move(buf));
+    } else {
+      std::unique_ptr<folly::IOBuf> remaining;
+      if (this->crtBuf_->length() - this->offset_ > 0) {
+        // Need to split current IOBuf in two.
+        remaining = this->crtBuf_->cloneOne();
+        remaining->trimStart(this->offset_);
+        nextBuf = remaining.get();
+        buf->prependChain(std::move(remaining));
+      } else {
+        // Can just append
+        nextBuf = this->crtBuf_->next();
+      }
+      this->crtBuf_->trimEnd(this->length());
+      this->crtBuf_->appendChain(std::move(buf));
+    }
+    // Jump past the new links
+    this->offset_ = 0;
+    this->crtBuf_ = nextBuf;
+  }
+
+  uint8_t* writableData() {
+    return this->crtBuf_->writableData() + this->offset_;
+  }
+
+ private:
+  void maybeUnshare() {
+    if (UNLIKELY(maybeShared_)) {
+      this->crtBuf_->unshareOne();
+      maybeShared_ = false;
+    }
+  }
+
+  void advanceDone() {
+    maybeShared_ = true;
+  }
+
+  bool maybeShared_;
+};
+
+typedef RWCursor<CursorAccess::PRIVATE> RWPrivateCursor;
+typedef RWCursor<CursorAccess::UNSHARE> RWUnshareCursor;
+
+/**
+ * Append to the end of a buffer chain, growing the chain (by allocating new
+ * buffers) in increments of at least growth bytes every time.  Won't grow
+ * (and push() and ensure() will throw) if growth == 0.
+ *
+ * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead
+ * of chaining.
+ */
+class Appender : public detail::Writable<Appender> {
+ public:
+  Appender(IOBuf* buf, uint64_t growth)
+    : buffer_(buf),
+      crtBuf_(buf->prev()),
+      growth_(growth) {
+  }
+
+  uint8_t* writableData() {
+    return crtBuf_->writableTail();
+  }
+
+  size_t length() const {
+    return crtBuf_->tailroom();
+  }
+
+  /**
+   * Mark n bytes (must be <= length()) as appended, as per the
+   * IOBuf::append() method.
+   */
+  void append(size_t n) {
+    crtBuf_->append(n);
+  }
+
+  /**
+   * Ensure at least n contiguous bytes available to write.
+   * Postcondition: length() >= n.
+   */
+  void ensure(uint64_t n) {
+    if (LIKELY(length() >= n)) {
+      return;
+    }
+
+    // Waste the rest of the current buffer and allocate a new one.
+    // Don't make it too small, either.
+    if (growth_ == 0) {
+      throw std::out_of_range("can't grow buffer chain");
+    }
+
+    n = std::max(n, growth_);
+    buffer_->prependChain(IOBuf::create(n));
+    crtBuf_ = buffer_->prev();
+  }
+
+  using detail::Writable<Appender>::pushAtMost;
+  size_t pushAtMost(const uint8_t* buf, size_t len) {
+    size_t copied = 0;
+    for (;;) {
+      // Fast path: it all fits in one buffer.
+      size_t available = length();
+      if (LIKELY(available >= len)) {
+        memcpy(writableData(), buf, len);
+        append(len);
+        return copied + len;
+      }
+
+      memcpy(writableData(), buf, available);
+      append(available);
+      copied += available;
+      if (UNLIKELY(!tryGrowChain())) {
+        return copied;
+      }
+      buf += available;
+      len -= available;
+    }
+  }
+
+  /*
+   * Append to the end of this buffer, using a printf() style
+   * format specifier.
+   *
+   * Note that folly/Format.h provides nicer and more type-safe mechanisms
+   * for formatting strings, which should generally be preferred over
+   * printf-style formatting.  Appender objects can be used directly as an
+   * output argument for Formatter objects.  For example:
+   *
+   *   Appender app(&iobuf);
+   *   format("{} {}", "hello", "world")(app);
+   *
+   * However, printf-style strings are still needed when dealing with existing
+   * third-party code in some cases.
+   *
+   * This will always add a nul-terminating character after the end
+   * of the output.  However, the buffer data length will only be updated to
+   * include the data itself.  The nul terminator will be the first byte in the
+   * buffer tailroom.
+   *
+   * This method may throw exceptions on error.
+   */
+  void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...)
+    FOLLY_PRINTF_FORMAT_ATTR(2, 3);
+
+  void vprintf(const char* fmt, va_list ap);
+
+  /*
+   * Calling an Appender object with a StringPiece will append the string
+   * piece.  This allows Appender objects to be used directly with
+   * Formatter.
+   */
+  void operator()(StringPiece sp) {
+    push(ByteRange(sp));
+  }
+
+ private:
+  bool tryGrowChain() {
+    assert(crtBuf_->next() == buffer_);
+    if (growth_ == 0) {
+      return false;
+    }
+
+    buffer_->prependChain(IOBuf::create(growth_));
+    crtBuf_ = buffer_->prev();
+    return true;
+  }
+
+  IOBuf* buffer_;
+  IOBuf* crtBuf_;
+  uint64_t growth_;
+};
+
+class QueueAppender : public detail::Writable<QueueAppender> {
+ public:
+  /**
+   * Create an Appender that writes to a IOBufQueue.  When we allocate
+   * space in the queue, we grow no more than growth bytes at once
+   * (unless you call ensure() with a bigger value yourself).
+   */
+  QueueAppender(IOBufQueue* queue, uint64_t growth) {
+    reset(queue, growth);
+  }
+
+  void reset(IOBufQueue* queue, uint64_t growth) {
+    queue_ = queue;
+    growth_ = growth;
+  }
+
+  uint8_t* writableData() {
+    return static_cast<uint8_t*>(queue_->writableTail());
+  }
+
+  size_t length() const { return queue_->tailroom(); }
+
+  void append(size_t n) { queue_->postallocate(n); }
+
+  // Ensure at least n contiguous; can go above growth_, throws if
+  // not enough room.
+  void ensure(uint64_t n) { queue_->preallocate(n, growth_); }
+
+  template <class T>
+  typename std::enable_if<std::is_arithmetic<T>::value>::type
+  write(T value) {
+    // We can't fail.
+    auto p = queue_->preallocate(sizeof(T), growth_);
+    storeUnaligned(p.first, value);
+    queue_->postallocate(sizeof(T));
+  }
+
+  using detail::Writable<QueueAppender>::pushAtMost;
+  size_t pushAtMost(const uint8_t* buf, size_t len) {
+    size_t remaining = len;
+    while (remaining != 0) {
+      auto p = queue_->preallocate(std::min(remaining, growth_),
+                                   growth_,
+                                   remaining);
+      memcpy(p.first, buf, p.second);
+      queue_->postallocate(p.second);
+      buf += p.second;
+      remaining -= p.second;
+    }
+
+    return len;
+  }
+
+  void insert(std::unique_ptr<folly::IOBuf> buf) {
+    if (buf) {
+      queue_->append(std::move(buf), true);
+    }
+  }
+
+ private:
+  folly::IOBufQueue* queue_;
+  size_t growth_;
+};
+
+}}  // folly::io
+
+#endif // FOLLY_CURSOR_H
diff --git a/faux-folly/folly/io/IOBuf.cpp b/faux-folly/folly/io/IOBuf.cpp
new file mode 100644
index 0000000..35c4e10
--- /dev/null
+++ b/faux-folly/folly/io/IOBuf.cpp
@@ -0,0 +1,970 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include <folly/io/IOBuf.h>
+
+#include <folly/Conv.h>
+#include <folly/Likely.h>
+#include <folly/Malloc.h>
+#include <folly/Memory.h>
+#include <folly/ScopeGuard.h>
+#include <folly/SpookyHashV2.h>
+#include <folly/io/Cursor.h>
+
+#include <stdexcept>
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+using std::unique_ptr;
+
+namespace {
+
+enum : uint16_t {
+  kHeapMagic = 0xa5a5,
+  // This memory segment contains an IOBuf that is still in use
+  kIOBufInUse = 0x01,
+  // This memory segment contains buffer data that is still in use
+  kDataInUse = 0x02,
+};
+
+enum : uint64_t {
+  // When create() is called for buffers less than kDefaultCombinedBufSize,
+  // we allocate a single combined memory segment for the IOBuf and the data
+  // together.  See the comments for createCombined()/createSeparate() for more
+  // details.
+  //
+  // (The size of 1k is largely just a guess here.  We could could probably do
+  // benchmarks of real applications to see if adjusting this number makes a
+  // difference.  Callers that know their exact use case can also explicitly
+  // call createCombined() or createSeparate().)
+  kDefaultCombinedBufSize = 1024
+};
+
+// Helper function for IOBuf::takeOwnership()
+void takeOwnershipError(bool freeOnError, void* buf,
+                        folly::IOBuf::FreeFunction freeFn,
+                        void* userData) {
+  if (!freeOnError) {
+    return;
+  }
+  if (!freeFn) {
+    free(buf);
+    return;
+  }
+  try {
+    freeFn(buf, userData);
+  } catch (...) {
+    // The user's free function is not allowed to throw.
+    // (We are already in the middle of throwing an exception, so
+    // we cannot let this exception go unhandled.)
+    abort();
+  }
+}
+
+} // unnamed namespace
+
+namespace folly {
+
+struct IOBuf::HeapPrefix {
+  HeapPrefix(uint16_t flg)
+    : magic(kHeapMagic),
+      flags(flg) {}
+  ~HeapPrefix() {
+    // Reset magic to 0 on destruction.  This is solely for debugging purposes
+    // to help catch bugs where someone tries to use HeapStorage after it has
+    // been deleted.
+    magic = 0;
+  }
+
+  uint16_t magic;
+  std::atomic<uint16_t> flags;
+};
+
+struct IOBuf::HeapStorage {
+  HeapPrefix prefix;
+  // The IOBuf is last in the HeapStorage object.
+  // This way operator new will work even if allocating a subclass of IOBuf
+  // that requires more space.
+  folly::IOBuf buf;
+};
+
+struct IOBuf::HeapFullStorage {
+  // Make sure jemalloc allocates from the 64-byte class.  Putting this here
+  // because HeapStorage is private so it can't be at namespace level.
+  static_assert(sizeof(HeapStorage) <= 64,
+                "IOBuf may not grow over 56 bytes!");
+
+  HeapStorage hs;
+  SharedInfo shared;
+  std::max_align_t align;
+};
+
+IOBuf::SharedInfo::SharedInfo()
+  : freeFn(nullptr),
+    userData(nullptr) {
+  // Use relaxed memory ordering here.  Since we are creating a new SharedInfo,
+  // no other threads should be referring to it yet.
+  refcount.store(1, std::memory_order_relaxed);
+}
+
+IOBuf::SharedInfo::SharedInfo(FreeFunction fn, void* arg)
+  : freeFn(fn),
+    userData(arg) {
+  // Use relaxed memory ordering here.  Since we are creating a new SharedInfo,
+  // no other threads should be referring to it yet.
+  refcount.store(1, std::memory_order_relaxed);
+}
+
+void* IOBuf::operator new(size_t size) {
+  size_t fullSize = offsetof(HeapStorage, buf) + size;
+  auto* storage = static_cast<HeapStorage*>(malloc(fullSize));
+  // operator new is not allowed to return NULL
+  if (UNLIKELY(storage == nullptr)) {
+    throw std::bad_alloc();
+  }
+
+  new (&storage->prefix) HeapPrefix(kIOBufInUse);
+  return &(storage->buf);
+}
+
+void* IOBuf::operator new(size_t size, void* ptr) {
+  return ptr;
+}
+
+void IOBuf::operator delete(void* ptr) {
+  auto* storageAddr = static_cast<uint8_t*>(ptr) - offsetof(HeapStorage, buf);
+  auto* storage = reinterpret_cast<HeapStorage*>(storageAddr);
+  releaseStorage(storage, kIOBufInUse);
+}
+
+void IOBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) {
+  CHECK_EQ(storage->prefix.magic, static_cast<uint16_t>(kHeapMagic));
+
+  // Use relaxed memory order here.  If we are unlucky and happen to get
+  // out-of-date data the compare_exchange_weak() call below will catch
+  // it and load new data with memory_order_acq_rel.
+  auto flags = storage->prefix.flags.load(std::memory_order_acquire);
+  DCHECK_EQ((flags & freeFlags), freeFlags);
+
+  while (true) {
+    uint16_t newFlags = (flags & ~freeFlags);
+    if (newFlags == 0) {
+      // The storage space is now unused.  Free it.
+      storage->prefix.HeapPrefix::~HeapPrefix();
+      free(storage);
+      return;
+    }
+
+    // This storage segment still contains portions that are in use.
+    // Just clear the flags specified in freeFlags for now.
+    auto ret = storage->prefix.flags.compare_exchange_weak(
+        flags, newFlags, std::memory_order_acq_rel);
+    if (ret) {
+      // We successfully updated the flags.
+      return;
+    }
+
+    // We failed to update the flags.  Some other thread probably updated them
+    // and cleared some of the other bits.  Continue around the loop to see if
+    // we are the last user now, or if we need to try updating the flags again.
+  }
+}
+
+void IOBuf::freeInternalBuf(void* buf, void* userData) {
+  auto* storage = static_cast<HeapStorage*>(userData);
+  releaseStorage(storage, kDataInUse);
+}
+
+IOBuf::IOBuf(CreateOp, uint64_t capacity)
+  : next_(this),
+    prev_(this),
+    data_(nullptr),
+    length_(0),
+    flagsAndSharedInfo_(0) {
+  SharedInfo* info;
+  allocExtBuffer(capacity, &buf_, &info, &capacity_);
+  setSharedInfo(info);
+  data_ = buf_;
+}
+
+IOBuf::IOBuf(CopyBufferOp op, const void* buf, uint64_t size,
+             uint64_t headroom, uint64_t minTailroom)
+  : IOBuf(CREATE, headroom + size + minTailroom) {
+  advance(headroom);
+  memcpy(writableData(), buf, size);
+  append(size);
+}
+
+IOBuf::IOBuf(CopyBufferOp op, ByteRange br,
+             uint64_t headroom, uint64_t minTailroom)
+  : IOBuf(op, br.data(), br.size(), headroom, minTailroom) {
+}
+
+unique_ptr<IOBuf> IOBuf::create(uint64_t capacity) {
+  // For smaller-sized buffers, allocate the IOBuf, SharedInfo, and the buffer
+  // all with a single allocation.
+  //
+  // We don't do this for larger buffers since it can be wasteful if the user
+  // needs to reallocate the buffer but keeps using the same IOBuf object.
+  // In this case we can't free the data space until the IOBuf is also
+  // destroyed.  Callers can explicitly call createCombined() or
+  // createSeparate() if they know their use case better, and know if they are
+  // likely to reallocate the buffer later.
+  if (capacity <= kDefaultCombinedBufSize) {
+    return createCombined(capacity);
+  }
+  return createSeparate(capacity);
+}
+
+unique_ptr<IOBuf> IOBuf::createCombined(uint64_t capacity) {
+  // To save a memory allocation, allocate space for the IOBuf object, the
+  // SharedInfo struct, and the data itself all with a single call to malloc().
+  size_t requiredStorage = offsetof(HeapFullStorage, align) + capacity;
+  size_t mallocSize = goodMallocSize(requiredStorage);
+  auto* storage = static_cast<HeapFullStorage*>(malloc(mallocSize));
+
+  new (&storage->hs.prefix) HeapPrefix(kIOBufInUse | kDataInUse);
+  new (&storage->shared) SharedInfo(freeInternalBuf, storage);
+
+  uint8_t* bufAddr = reinterpret_cast<uint8_t*>(&storage->align);
+  uint8_t* storageEnd = reinterpret_cast<uint8_t*>(storage) + mallocSize;
+  size_t actualCapacity = storageEnd - bufAddr;
+  unique_ptr<IOBuf> ret(new (&storage->hs.buf) IOBuf(
+        InternalConstructor(), packFlagsAndSharedInfo(0, &storage->shared),
+        bufAddr, actualCapacity, bufAddr, 0));
+  return ret;
+}
+
+unique_ptr<IOBuf> IOBuf::createSeparate(uint64_t capacity) {
+  return make_unique<IOBuf>(CREATE, capacity);
+}
+
+unique_ptr<IOBuf> IOBuf::createChain(
+    size_t totalCapacity, uint64_t maxBufCapacity) {
+  unique_ptr<IOBuf> out = create(
+      std::min(totalCapacity, size_t(maxBufCapacity)));
+  size_t allocatedCapacity = out->capacity();
+
+  while (allocatedCapacity < totalCapacity) {
+    unique_ptr<IOBuf> newBuf = create(
+        std::min(totalCapacity - allocatedCapacity, size_t(maxBufCapacity)));
+    allocatedCapacity += newBuf->capacity();
+    out->prependChain(std::move(newBuf));
+  }
+
+  return out;
+}
+
+IOBuf::IOBuf(TakeOwnershipOp, void* buf, uint64_t capacity, uint64_t length,
+             FreeFunction freeFn, void* userData,
+             bool freeOnError)
+  : next_(this),
+    prev_(this),
+    data_(static_cast<uint8_t*>(buf)),
+    buf_(static_cast<uint8_t*>(buf)),
+    length_(length),
+    capacity_(capacity),
+    flagsAndSharedInfo_(packFlagsAndSharedInfo(kFlagFreeSharedInfo, nullptr)) {
+  try {
+    setSharedInfo(new SharedInfo(freeFn, userData));
+  } catch (...) {
+    takeOwnershipError(freeOnError, buf, freeFn, userData);
+    throw;
+  }
+}
+
+unique_ptr<IOBuf> IOBuf::takeOwnership(void* buf, uint64_t capacity,
+                                       uint64_t length,
+                                       FreeFunction freeFn,
+                                       void* userData,
+                                       bool freeOnError) {
+  try {
+    // TODO: We could allocate the IOBuf object and SharedInfo all in a single
+    // memory allocation.  We could use the existing HeapStorage class, and
+    // define a new kSharedInfoInUse flag.  We could change our code to call
+    // releaseStorage(kFlagFreeSharedInfo) when this kFlagFreeSharedInfo,
+    // rather than directly calling delete.
+    //
+    // Note that we always pass freeOnError as false to the constructor.
+    // If the constructor throws we'll handle it below.  (We have to handle
+    // allocation failures from make_unique too.)
+    return make_unique<IOBuf>(TAKE_OWNERSHIP, buf, capacity, length,
+                              freeFn, userData, false);
+  } catch (...) {
+    takeOwnershipError(freeOnError, buf, freeFn, userData);
+    throw;
+  }
+}
+
+IOBuf::IOBuf(WrapBufferOp, const void* buf, uint64_t capacity)
+  : IOBuf(InternalConstructor(), 0,
+          // We cast away the const-ness of the buffer here.
+          // This is okay since IOBuf users must use unshare() to create a copy
+          // of this buffer before writing to the buffer.
+          static_cast<uint8_t*>(const_cast<void*>(buf)), capacity,
+          static_cast<uint8_t*>(const_cast<void*>(buf)), capacity) {
+}
+
+IOBuf::IOBuf(WrapBufferOp op, ByteRange br)
+  : IOBuf(op, br.data(), br.size()) {
+}
+
+unique_ptr<IOBuf> IOBuf::wrapBuffer(const void* buf, uint64_t capacity) {
+  return make_unique<IOBuf>(WRAP_BUFFER, buf, capacity);
+}
+
+IOBuf::IOBuf() noexcept {
+}
+
+IOBuf::IOBuf(IOBuf&& other) noexcept {
+  *this = std::move(other);
+}
+
+IOBuf::IOBuf(const IOBuf& other) {
+  other.cloneInto(*this);
+}
+
+IOBuf::IOBuf(InternalConstructor,
+             uintptr_t flagsAndSharedInfo,
+             uint8_t* buf,
+             uint64_t capacity,
+             uint8_t* data,
+             uint64_t length)
+  : next_(this),
+    prev_(this),
+    data_(data),
+    buf_(buf),
+    length_(length),
+    capacity_(capacity),
+    flagsAndSharedInfo_(flagsAndSharedInfo) {
+  assert(data >= buf);
+  assert(data + length <= buf + capacity);
+}
+
+IOBuf::~IOBuf() {
+  // Destroying an IOBuf destroys the entire chain.
+  // Users of IOBuf should only explicitly delete the head of any chain.
+  // The other elements in the chain will be automatically destroyed.
+  while (next_ != this) {
+    // Since unlink() returns unique_ptr() and we don't store it,
+    // it will automatically delete the unlinked element.
+    (void)next_->unlink();
+  }
+
+  decrementRefcount();
+}
+
+IOBuf& IOBuf::operator=(IOBuf&& other) noexcept {
+  if (this == &other) {
+    return *this;
+  }
+
+  // If we are part of a chain, delete the rest of the chain.
+  while (next_ != this) {
+    // Since unlink() returns unique_ptr() and we don't store it,
+    // it will automatically delete the unlinked element.
+    (void)next_->unlink();
+  }
+
+  // Decrement our refcount on the current buffer
+  decrementRefcount();
+
+  // Take ownership of the other buffer's data
+  data_ = other.data_;
+  buf_ = other.buf_;
+  length_ = other.length_;
+  capacity_ = other.capacity_;
+  flagsAndSharedInfo_ = other.flagsAndSharedInfo_;
+  // Reset other so it is a clean state to be destroyed.
+  other.data_ = nullptr;
+  other.buf_ = nullptr;
+  other.length_ = 0;
+  other.capacity_ = 0;
+  other.flagsAndSharedInfo_ = 0;
+
+  // If other was part of the chain, assume ownership of the rest of its chain.
+  // (It's only valid to perform move assignment on the head of a chain.)
+  if (other.next_ != &other) {
+    next_ = other.next_;
+    next_->prev_ = this;
+    other.next_ = &other;
+
+    prev_ = other.prev_;
+    prev_->next_ = this;
+    other.prev_ = &other;
+  }
+
+  // Sanity check to make sure that other is in a valid state to be destroyed.
+  DCHECK_EQ(other.prev_, &other);
+  DCHECK_EQ(other.next_, &other);
+
+  return *this;
+}
+
+IOBuf& IOBuf::operator=(const IOBuf& other) {
+  if (this != &other) {
+    *this = IOBuf(other);
+  }
+  return *this;
+}
+
+bool IOBuf::empty() const {
+  const IOBuf* current = this;
+  do {
+    if (current->length() != 0) {
+      return false;
+    }
+    current = current->next_;
+  } while (current != this);
+  return true;
+}
+
+size_t IOBuf::countChainElements() const {
+  size_t numElements = 1;
+  for (IOBuf* current = next_; current != this; current = current->next_) {
+    ++numElements;
+  }
+  return numElements;
+}
+
+uint64_t IOBuf::computeChainDataLength() const {
+  uint64_t fullLength = length_;
+  for (IOBuf* current = next_; current != this; current = current->next_) {
+    fullLength += current->length_;
+  }
+  return fullLength;
+}
+
+void IOBuf::prependChain(unique_ptr<IOBuf>&& iobuf) {
+  // Take ownership of the specified IOBuf
+  IOBuf* other = iobuf.release();
+
+  // Remember the pointer to the tail of the other chain
+  IOBuf* otherTail = other->prev_;
+
+  // Hook up prev_->next_ to point at the start of the other chain,
+  // and other->prev_ to point at prev_
+  prev_->next_ = other;
+  other->prev_ = prev_;
+
+  // Hook up otherTail->next_ to point at us,
+  // and prev_ to point back at otherTail,
+  otherTail->next_ = this;
+  prev_ = otherTail;
+}
+
+unique_ptr<IOBuf> IOBuf::clone() const {
+  unique_ptr<IOBuf> ret = make_unique<IOBuf>();
+  cloneInto(*ret);
+  return ret;
+}
+
+unique_ptr<IOBuf> IOBuf::cloneOne() const {
+  unique_ptr<IOBuf> ret = make_unique<IOBuf>();
+  cloneOneInto(*ret);
+  return ret;
+}
+
+void IOBuf::cloneInto(IOBuf& other) const {
+  IOBuf tmp;
+  cloneOneInto(tmp);
+
+  for (IOBuf* current = next_; current != this; current = current->next_) {
+    tmp.prependChain(current->cloneOne());
+  }
+
+  other = std::move(tmp);
+}
+
+void IOBuf::cloneOneInto(IOBuf& other) const {
+  SharedInfo* info = sharedInfo();
+  if (info) {
+    setFlags(kFlagMaybeShared);
+  }
+  other = IOBuf(InternalConstructor(),
+                flagsAndSharedInfo_, buf_, capacity_,
+                data_, length_);
+  if (info) {
+    info->refcount.fetch_add(1, std::memory_order_acq_rel);
+  }
+}
+
+void IOBuf::unshareOneSlow() {
+  // Allocate a new buffer for the data
+  uint8_t* buf;
+  SharedInfo* sharedInfo;
+  uint64_t actualCapacity;
+  allocExtBuffer(capacity_, &buf, &sharedInfo, &actualCapacity);
+
+  // Copy the data
+  // Maintain the same amount of headroom.  Since we maintained the same
+  // minimum capacity we also maintain at least the same amount of tailroom.
+  uint64_t headlen = headroom();
+  memcpy(buf + headlen, data_, length_);
+
+  // Release our reference on the old buffer
+  decrementRefcount();
+  // Make sure kFlagMaybeShared and kFlagFreeSharedInfo are all cleared.
+  setFlagsAndSharedInfo(0, sharedInfo);
+
+  // Update the buffer pointers to point to the new buffer
+  data_ = buf + headlen;
+  buf_ = buf;
+}
+
+void IOBuf::unshareChained() {
+  // unshareChained() should only be called if we are part of a chain of
+  // multiple IOBufs.  The caller should have already verified this.
+  assert(isChained());
+
+  IOBuf* current = this;
+  while (true) {
+    if (current->isSharedOne()) {
+      // we have to unshare
+      break;
+    }
+
+    current = current->next_;
+    if (current == this) {
+      // None of the IOBufs in the chain are shared,
+      // so return without doing anything
+      return;
+    }
+  }
+
+  // We have to unshare.  Let coalesceSlow() do the work.
+  coalesceSlow();
+}
+
+void IOBuf::makeManagedChained() {
+  assert(isChained());
+
+  IOBuf* current = this;
+  while (true) {
+    current->makeManagedOne();
+    current = current->next_;
+    if (current == this) {
+      break;
+    }
+  }
+}
+
+void IOBuf::coalesceSlow() {
+  // coalesceSlow() should only be called if we are part of a chain of multiple
+  // IOBufs.  The caller should have already verified this.
+  DCHECK(isChained());
+
+  // Compute the length of the entire chain
+  uint64_t newLength = 0;
+  IOBuf* end = this;
+  do {
+    newLength += end->length_;
+    end = end->next_;
+  } while (end != this);
+
+  coalesceAndReallocate(newLength, end);
+  // We should be only element left in the chain now
+  DCHECK(!isChained());
+}
+
+void IOBuf::coalesceSlow(size_t maxLength) {
+  // coalesceSlow() should only be called if we are part of a chain of multiple
+  // IOBufs.  The caller should have already verified this.
+  DCHECK(isChained());
+  DCHECK_LT(length_, maxLength);
+
+  // Compute the length of the entire chain
+  uint64_t newLength = 0;
+  IOBuf* end = this;
+  while (true) {
+    newLength += end->length_;
+    end = end->next_;
+    if (newLength >= maxLength) {
+      break;
+    }
+    if (end == this) {
+      throw std::overflow_error("attempted to coalesce more data than "
+                                "available");
+    }
+  }
+
+  coalesceAndReallocate(newLength, end);
+  // We should have the requested length now
+  DCHECK_GE(length_, maxLength);
+}
+
+void IOBuf::coalesceAndReallocate(size_t newHeadroom,
+                                  size_t newLength,
+                                  IOBuf* end,
+                                  size_t newTailroom) {
+  uint64_t newCapacity = newLength + newHeadroom + newTailroom;
+
+  // Allocate space for the coalesced buffer.
+  // We always convert to an external buffer, even if we happened to be an
+  // internal buffer before.
+  uint8_t* newBuf;
+  SharedInfo* newInfo;
+  uint64_t actualCapacity;
+  allocExtBuffer(newCapacity, &newBuf, &newInfo, &actualCapacity);
+
+  // Copy the data into the new buffer
+  uint8_t* newData = newBuf + newHeadroom;
+  uint8_t* p = newData;
+  IOBuf* current = this;
+  size_t remaining = newLength;
+  do {
+    assert(current->length_ <= remaining);
+    remaining -= current->length_;
+    memcpy(p, current->data_, current->length_);
+    p += current->length_;
+    current = current->next_;
+  } while (current != end);
+  assert(remaining == 0);
+
+  // Point at the new buffer
+  decrementRefcount();
+
+  // Make sure kFlagMaybeShared and kFlagFreeSharedInfo are all cleared.
+  setFlagsAndSharedInfo(0, newInfo);
+
+  capacity_ = actualCapacity;
+  buf_ = newBuf;
+  data_ = newData;
+  length_ = newLength;
+
+  // Separate from the rest of our chain.
+  // Since we don't store the unique_ptr returned by separateChain(),
+  // this will immediately delete the returned subchain.
+  if (isChained()) {
+    (void)separateChain(next_, current->prev_);
+  }
+}
+
+void IOBuf::decrementRefcount() {
+  // Externally owned buffers don't have a SharedInfo object and aren't managed
+  // by the reference count
+  SharedInfo* info = sharedInfo();
+  if (!info) {
+    return;
+  }
+
+  // Decrement the refcount
+  uint32_t newcnt = info->refcount.fetch_sub(
+      1, std::memory_order_acq_rel);
+  // Note that fetch_sub() returns the value before we decremented.
+  // If it is 1, we were the only remaining user; if it is greater there are
+  // still other users.
+  if (newcnt > 1) {
+    return;
+  }
+
+  // We were the last user.  Free the buffer
+  freeExtBuffer();
+
+  // Free the SharedInfo if it was allocated separately.
+  //
+  // This is only used by takeOwnership().
+  //
+  // To avoid this special case handling in decrementRefcount(), we could have
+  // takeOwnership() set a custom freeFn() that calls the user's free function
+  // then frees the SharedInfo object.  (This would require that
+  // takeOwnership() store the user's free function with its allocated
+  // SharedInfo object.)  However, handling this specially with a flag seems
+  // like it shouldn't be problematic.
+  if (flags() & kFlagFreeSharedInfo) {
+    delete sharedInfo();
+  }
+}
+
+void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) {
+  size_t newCapacity = (size_t)length_ + minHeadroom + minTailroom;
+  DCHECK_LT(newCapacity, UINT32_MAX);
+
+  // reserveSlow() is dangerous if anyone else is sharing the buffer, as we may
+  // reallocate and free the original buffer.  It should only ever be called if
+  // we are the only user of the buffer.
+  DCHECK(!isSharedOne());
+
+  // We'll need to reallocate the buffer.
+  // There are a few options.
+  // - If we have enough total room, move the data around in the buffer
+  //   and adjust the data_ pointer.
+  // - If we're using an internal buffer, we'll switch to an external
+  //   buffer with enough headroom and tailroom.
+  // - If we have enough headroom (headroom() >= minHeadroom) but not too much
+  //   (so we don't waste memory), we can try one of two things, depending on
+  //   whether we use jemalloc or not:
+  //   - If using jemalloc, we can try to expand in place, avoiding a memcpy()
+  //   - If not using jemalloc and we don't have too much to copy,
+  //     we'll use realloc() (note that realloc might have to copy
+  //     headroom + data + tailroom, see smartRealloc in folly/Malloc.h)
+  // - Otherwise, bite the bullet and reallocate.
+  if (headroom() + tailroom() >= minHeadroom + minTailroom) {
+    uint8_t* newData = writableBuffer() + minHeadroom;
+    memmove(newData, data_, length_);
+    data_ = newData;
+    return;
+  }
+
+  size_t newAllocatedCapacity = 0;
+  uint8_t* newBuffer = nullptr;
+  uint64_t newHeadroom = 0;
+  uint64_t oldHeadroom = headroom();
+
+  // If we have a buffer allocated with malloc and we just need more tailroom,
+  // try to use realloc()/xallocx() to grow the buffer in place.
+  SharedInfo* info = sharedInfo();
+  if (info && (info->freeFn == nullptr) && length_ != 0 &&
+      oldHeadroom >= minHeadroom) {
+    size_t headSlack = oldHeadroom - minHeadroom;
+    newAllocatedCapacity = goodExtBufferSize(newCapacity + headSlack);
+    if (usingJEMalloc()) {
+      // We assume that tailroom is more useful and more important than
+      // headroom (not least because realloc / xallocx allow us to grow the
+      // buffer at the tail, but not at the head)  So, if we have more headroom
+      // than we need, we consider that "wasted".  We arbitrarily define "too
+      // much" headroom to be 25% of the capacity.
+      if (headSlack * 4 <= newCapacity) {
+        size_t allocatedCapacity = capacity() + sizeof(SharedInfo);
+        void* p = buf_;
+        if (allocatedCapacity >= jemallocMinInPlaceExpandable) {
+          if (xallocx(p, newAllocatedCapacity, 0, 0) == newAllocatedCapacity) {
+            newBuffer = static_cast<uint8_t*>(p);
+            newHeadroom = oldHeadroom;
+          }
+          // if xallocx failed, do nothing, fall back to malloc/memcpy/free
+        }
+      }
+    } else {  // Not using jemalloc
+      size_t copySlack = capacity() - length_;
+      if (copySlack * 2 <= length_) {
+        void* p = realloc(buf_, newAllocatedCapacity);
+        if (UNLIKELY(p == nullptr)) {
+          throw std::bad_alloc();
+        }
+        newBuffer = static_cast<uint8_t*>(p);
+        newHeadroom = oldHeadroom;
+      }
+    }
+  }
+
+  // None of the previous reallocation strategies worked (or we're using
+  // an internal buffer).  malloc/copy/free.
+  if (newBuffer == nullptr) {
+    newAllocatedCapacity = goodExtBufferSize(newCapacity);
+    void* p = malloc(newAllocatedCapacity);
+    if (UNLIKELY(p == nullptr)) {
+      throw std::bad_alloc();
+    }
+    newBuffer = static_cast<uint8_t*>(p);
+    memcpy(newBuffer + minHeadroom, data_, length_);
+    if (sharedInfo()) {
+      freeExtBuffer();
+    }
+    newHeadroom = minHeadroom;
+  }
+
+  uint64_t cap;
+  initExtBuffer(newBuffer, newAllocatedCapacity, &info, &cap);
+
+  if (flags() & kFlagFreeSharedInfo) {
+    delete sharedInfo();
+  }
+
+  setFlagsAndSharedInfo(0, info);
+  capacity_ = cap;
+  buf_ = newBuffer;
+  data_ = newBuffer + newHeadroom;
+  // length_ is unchanged
+}
+
+void IOBuf::freeExtBuffer() {
+  SharedInfo* info = sharedInfo();
+  DCHECK(info);
+
+  if (info->freeFn) {
+    try {
+      info->freeFn(buf_, info->userData);
+    } catch (...) {
+      // The user's free function should never throw.  Otherwise we might
+      // throw from the IOBuf destructor.  Other code paths like coalesce()
+      // also assume that decrementRefcount() cannot throw.
+      abort();
+    }
+  } else {
+    free(buf_);
+  }
+}
+
+void IOBuf::allocExtBuffer(uint64_t minCapacity,
+                           uint8_t** bufReturn,
+                           SharedInfo** infoReturn,
+                           uint64_t* capacityReturn) {
+  size_t mallocSize = goodExtBufferSize(minCapacity);
+  uint8_t* buf = static_cast<uint8_t*>(malloc(mallocSize));
+  if (UNLIKELY(buf == nullptr)) {
+    throw std::bad_alloc();
+  }
+  initExtBuffer(buf, mallocSize, infoReturn, capacityReturn);
+  *bufReturn = buf;
+}
+
+size_t IOBuf::goodExtBufferSize(uint64_t minCapacity) {
+  // Determine how much space we should allocate.  We'll store the SharedInfo
+  // for the external buffer just after the buffer itself.  (We store it just
+  // after the buffer rather than just before so that the code can still just
+  // use free(buf_) to free the buffer.)
+  size_t minSize = static_cast<size_t>(minCapacity) + sizeof(SharedInfo);
+  // Add room for padding so that the SharedInfo will be aligned on an 8-byte
+  // boundary.
+  minSize = (minSize + 7) & ~7;
+
+  // Use goodMallocSize() to bump up the capacity to a decent size to request
+  // from malloc, so we can use all of the space that malloc will probably give
+  // us anyway.
+  return goodMallocSize(minSize);
+}
+
+void IOBuf::initExtBuffer(uint8_t* buf, size_t mallocSize,
+                          SharedInfo** infoReturn,
+                          uint64_t* capacityReturn) {
+  // Find the SharedInfo storage at the end of the buffer
+  // and construct the SharedInfo.
+  uint8_t* infoStart = (buf + mallocSize) - sizeof(SharedInfo);
+  SharedInfo* sharedInfo = new(infoStart) SharedInfo;
+
+  *capacityReturn = infoStart - buf;
+  *infoReturn = sharedInfo;
+}
+
+fbstring IOBuf::moveToFbString() {
+  // malloc-allocated buffers are just fine, everything else needs
+  // to be turned into one.
+  if (!sharedInfo() ||         // user owned, not ours to give up
+      sharedInfo()->freeFn ||  // not malloc()-ed
+      headroom() != 0 ||       // malloc()-ed block doesn't start at beginning
+      tailroom() == 0 ||       // no room for NUL terminator
+      isShared() ||            // shared
+      isChained()) {           // chained
+    // We might as well get rid of all head and tailroom if we're going
+    // to reallocate; we need 1 byte for NUL terminator.
+    coalesceAndReallocate(0, computeChainDataLength(), this, 1);
+  }
+
+  // Ensure NUL terminated
+  *writableTail() = 0;
+  fbstring str(reinterpret_cast<char*>(writableData()),
+               length(),  capacity(),
+               AcquireMallocatedString());
+
+  if (flags() & kFlagFreeSharedInfo) {
+    delete sharedInfo();
+  }
+
+  // Reset to a state where we can be deleted cleanly
+  flagsAndSharedInfo_ = 0;
+  buf_ = nullptr;
+  clear();
+  return str;
+}
+
+IOBuf::Iterator IOBuf::cbegin() const {
+  return Iterator(this, this);
+}
+
+IOBuf::Iterator IOBuf::cend() const {
+  return Iterator(nullptr, nullptr);
+}
+
+folly::fbvector<struct iovec> IOBuf::getIov() const {
+  folly::fbvector<struct iovec> iov;
+  iov.reserve(countChainElements());
+  appendToIov(&iov);
+  return iov;
+}
+
+void IOBuf::appendToIov(folly::fbvector<struct iovec>* iov) const {
+  IOBuf const* p = this;
+  do {
+    // some code can get confused by empty iovs, so skip them
+    if (p->length() > 0) {
+      iov->push_back({(void*)p->data(), folly::to<size_t>(p->length())});
+    }
+    p = p->next();
+  } while (p != this);
+}
+
+size_t IOBuf::fillIov(struct iovec* iov, size_t len) const {
+  IOBuf const* p = this;
+  size_t i = 0;
+  while (i < len) {
+    // some code can get confused by empty iovs, so skip them
+    if (p->length() > 0) {
+      iov[i].iov_base = const_cast<uint8_t*>(p->data());
+      iov[i].iov_len = p->length();
+      i++;
+    }
+    p = p->next();
+    if (p == this) {
+      return i;
+    }
+  }
+  return 0;
+}
+
+size_t IOBufHash::operator()(const IOBuf& buf) const {
+  folly::hash::SpookyHashV2 hasher;
+  hasher.Init(0, 0);
+  io::Cursor cursor(&buf);
+  for (;;) {
+    auto p = cursor.peek();
+    if (p.second == 0) {
+      break;
+    }
+    hasher.Update(p.first, p.second);
+    cursor.skip(p.second);
+  }
+  uint64_t h1;
+  uint64_t h2;
+  hasher.Final(&h1, &h2);
+  return h1;
+}
+
+bool IOBufEqual::operator()(const IOBuf& a, const IOBuf& b) const {
+  io::Cursor ca(&a);
+  io::Cursor cb(&b);
+  for (;;) {
+    auto pa = ca.peek();
+    auto pb = cb.peek();
+    if (pa.second == 0 && pb.second == 0) {
+      return true;
+    } else if (pa.second == 0 || pb.second == 0) {
+      return false;
+    }
+    size_t n = std::min(pa.second, pb.second);
+    DCHECK_GT(n, 0);
+    if (memcmp(pa.first, pb.first, n)) {
+      return false;
+    }
+    ca.skip(n);
+    cb.skip(n);
+  }
+}
+
+} // folly
diff --git a/faux-folly/folly/io/IOBuf.h b/faux-folly/folly/io/IOBuf.h
new file mode 100644
index 0000000..bb236d4
--- /dev/null
+++ b/faux-folly/folly/io/IOBuf.h
@@ -0,0 +1,1471 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_IO_IOBUF_H_
+#define FOLLY_IO_IOBUF_H_
+
+#include <glog/logging.h>
+#include <atomic>
+#include <cassert>
+#include <cinttypes>
+#include <cstddef>
+#include <cstring>
+#include <memory>
+#include <limits>
+#include <sys/uio.h>
+#include <type_traits>
+
+#include <boost/iterator/iterator_facade.hpp>
+
+#include <folly/FBString.h>
+#include <folly/Range.h>
+#include <folly/FBVector.h>
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
+namespace folly {
+
+/**
+ * An IOBuf is a pointer to a buffer of data.
+ *
+ * IOBuf objects are intended to be used primarily for networking code, and are
+ * modelled somewhat after FreeBSD's mbuf data structure, and Linux's sk_buff
+ * structure.
+ *
+ * IOBuf objects facilitate zero-copy network programming, by allowing multiple
+ * IOBuf objects to point to the same underlying buffer of data, using a
+ * reference count to track when the buffer is no longer needed and can be
+ * freed.
+ *
+ *
+ * Data Layout
+ * -----------
+ *
+ * The IOBuf itself is a small object containing a pointer to the buffer and
+ * information about which segment of the buffer contains valid data.
+ *
+ * The data layout looks like this:
+ *
+ *  +-------+
+ *  | IOBuf |
+ *  +-------+
+ *   /
+ *  |
+ *  v
+ *  +------------+--------------------+-----------+
+ *  | headroom   |        data        |  tailroom |
+ *  +------------+--------------------+-----------+
+ *  ^            ^                    ^           ^
+ *  buffer()   data()               tail()      bufferEnd()
+ *
+ *  The length() method returns the length of the valid data; capacity()
+ *  returns the entire capacity of the buffer (from buffer() to bufferEnd()).
+ *  The headroom() and tailroom() methods return the amount of unused capacity
+ *  available before and after the data.
+ *
+ *
+ * Buffer Sharing
+ * --------------
+ *
+ * The buffer itself is reference counted, and multiple IOBuf objects may point
+ * to the same buffer.  Each IOBuf may point to a different section of valid
+ * data within the underlying buffer.  For example, if multiple protocol
+ * requests are read from the network into a single buffer, a separate IOBuf
+ * may be created for each request, all sharing the same underlying buffer.
+ *
+ * In other words, when multiple IOBufs share the same underlying buffer, the
+ * data() and tail() methods on each IOBuf may point to a different segment of
+ * the data.  However, the buffer() and bufferEnd() methods will point to the
+ * same location for all IOBufs sharing the same underlying buffer.
+ *
+ *       +-----------+     +---------+
+ *       |  IOBuf 1  |     | IOBuf 2 |
+ *       +-----------+     +---------+
+ *        |         | _____/        |
+ *   data |    tail |/    data      | tail
+ *        v         v               v
+ *  +-------------------------------------+
+ *  |     |         |               |     |
+ *  +-------------------------------------+
+ *
+ * If you only read data from an IOBuf, you don't need to worry about other
+ * IOBuf objects possibly sharing the same underlying buffer.  However, if you
+ * ever write to the buffer you need to first ensure that no other IOBufs point
+ * to the same buffer.  The unshare() method may be used to ensure that you
+ * have an unshared buffer.
+ *
+ *
+ * IOBuf Chains
+ * ------------
+ *
+ * IOBuf objects also contain pointers to next and previous IOBuf objects.
+ * This can be used to represent a single logical piece of data that its stored
+ * in non-contiguous chunks in separate buffers.
+ *
+ * A single IOBuf object can only belong to one chain at a time.
+ *
+ * IOBuf chains are always circular.  The "prev" pointer in the head of the
+ * chain points to the tail of the chain.  However, it is up to the user to
+ * decide which IOBuf is the head.  Internally the IOBuf code does not care
+ * which element is the head.
+ *
+ * The lifetime of all IOBufs in the chain are linked: when one element in the
+ * chain is deleted, all other chained elements are also deleted.  Conceptually
+ * it is simplest to treat this as if the head of the chain owns all other
+ * IOBufs in the chain.  When you delete the head of the chain, it will delete
+ * the other elements as well.  For this reason, prependChain() and
+ * appendChain() take ownership of of the new elements being added to this
+ * chain.
+ *
+ * When the coalesce() method is used to coalesce an entire IOBuf chain into a
+ * single IOBuf, all other IOBufs in the chain are eliminated and automatically
+ * deleted.  The unshare() method may coalesce the chain; if it does it will
+ * similarly delete all IOBufs eliminated from the chain.
+ *
+ * As discussed in the following section, it is up to the user to maintain a
+ * lock around the entire IOBuf chain if multiple threads need to access the
+ * chain.  IOBuf does not provide any internal locking.
+ *
+ *
+ * Synchronization
+ * ---------------
+ *
+ * When used in multithread programs, a single IOBuf object should only be used
+ * in a single thread at a time.  If a caller uses a single IOBuf across
+ * multiple threads the caller is responsible for using an external lock to
+ * synchronize access to the IOBuf.
+ *
+ * Two separate IOBuf objects may be accessed concurrently in separate threads
+ * without locking, even if they point to the same underlying buffer.  The
+ * buffer reference count is always accessed atomically, and no other
+ * operations should affect other IOBufs that point to the same data segment.
+ * The caller is responsible for using unshare() to ensure that the data buffer
+ * is not shared by other IOBufs before writing to it, and this ensures that
+ * the data itself is not modified in one thread while also being accessed from
+ * another thread.
+ *
+ * For IOBuf chains, no two IOBufs in the same chain should be accessed
+ * simultaneously in separate threads.  The caller must maintain a lock around
+ * the entire chain if the chain, or individual IOBufs in the chain, may be
+ * accessed by multiple threads.
+ *
+ *
+ * IOBuf Object Allocation
+ * -----------------------
+ *
+ * IOBuf objects themselves exist separately from the data buffer they point
+ * to.  Therefore one must also consider how to allocate and manage the IOBuf
+ * objects.
+ *
+ * It is more common to allocate IOBuf objects on the heap, using the create(),
+ * takeOwnership(), or wrapBuffer() factory functions.  The clone()/cloneOne()
+ * functions also return new heap-allocated IOBufs.  The createCombined()
+ * function allocates the IOBuf object and data storage space together, in a
+ * single memory allocation.  This can improve performance, particularly if you
+ * know that the data buffer and the IOBuf itself will have similar lifetimes.
+ *
+ * That said, it is also possible to allocate IOBufs on the stack or inline
+ * inside another object as well.  This is useful for cases where the IOBuf is
+ * short-lived, or when the overhead of allocating the IOBuf on the heap is
+ * undesirable.
+ *
+ * However, note that stack-allocated IOBufs may only be used as the head of a
+ * chain (or standalone as the only IOBuf in a chain).  All non-head members of
+ * an IOBuf chain must be heap allocated.  (All functions to add nodes to a
+ * chain require a std::unique_ptr<IOBuf>, which enforces this requrement.)
+ *
+ * Copying IOBufs is only meaningful for the head of a chain. The entire chain
+ * is cloned; the IOBufs will become shared, and the old and new IOBufs will
+ * refer to the same underlying memory.
+ *
+ * IOBuf Sharing
+ * -------------
+ *
+ * The IOBuf class manages sharing of the underlying buffer that it points to,
+ * maintaining a reference count if multiple IOBufs are pointing at the same
+ * buffer.
+ *
+ * However, it is the callers responsibility to manage sharing and ownership of
+ * IOBuf objects themselves.  The IOBuf structure does not provide room for an
+ * intrusive refcount on the IOBuf object itself, only the underlying data
+ * buffer is reference counted.  If users want to share the same IOBuf object
+ * between multiple parts of the code, they are responsible for managing this
+ * sharing on their own.  (For example, by using a shared_ptr.  Alternatively,
+ * users always have the option of using clone() to create a second IOBuf that
+ * points to the same underlying buffer.)
+ */
+namespace detail {
+// Is T a unique_ptr<> to a standard-layout type?
+template <class T, class Enable=void> struct IsUniquePtrToSL
+  : public std::false_type { };
+template <class T, class D>
+struct IsUniquePtrToSL<
+  std::unique_ptr<T, D>,
+  typename std::enable_if<std::is_standard_layout<T>::value>::type>
+  : public std::true_type { };
+}  // namespace detail
+
+class IOBuf {
+ public:
+  class Iterator;
+
+  enum CreateOp { CREATE };
+  enum WrapBufferOp { WRAP_BUFFER };
+  enum TakeOwnershipOp { TAKE_OWNERSHIP };
+  enum CopyBufferOp { COPY_BUFFER };
+
+  typedef ByteRange value_type;
+  typedef Iterator iterator;
+  typedef Iterator const_iterator;
+
+  typedef void (*FreeFunction)(void* buf, void* userData);
+
+  /**
+   * Allocate a new IOBuf object with the requested capacity.
+   *
+   * Returns a new IOBuf object that must be (eventually) deleted by the
+   * caller.  The returned IOBuf may actually have slightly more capacity than
+   * requested.
+   *
+   * The data pointer will initially point to the start of the newly allocated
+   * buffer, and will have a data length of 0.
+   *
+   * Throws std::bad_alloc on error.
+   */
+  static std::unique_ptr<IOBuf> create(uint64_t capacity);
+  IOBuf(CreateOp, uint64_t capacity);
+
+  /**
+   * Create a new IOBuf, using a single memory allocation to allocate space
+   * for both the IOBuf object and the data storage space.
+   *
+   * This saves one memory allocation.  However, it can be wasteful if you
+   * later need to grow the buffer using reserve().  If the buffer needs to be
+   * reallocated, the space originally allocated will not be freed() until the
+   * IOBuf object itself is also freed.  (It can also be slightly wasteful in
+   * some cases where you clone this IOBuf and then free the original IOBuf.)
+   */
+  static std::unique_ptr<IOBuf> createCombined(uint64_t capacity);
+
+  /**
+   * Create a new IOBuf, using separate memory allocations for the IOBuf object
+   * for the IOBuf and the data storage space.
+   *
+   * This requires two memory allocations, but saves space in the long run
+   * if you know that you will need to reallocate the data buffer later.
+   */
+  static std::unique_ptr<IOBuf> createSeparate(uint64_t capacity);
+
+  /**
+   * Allocate a new IOBuf chain with the requested total capacity, allocating
+   * no more than maxBufCapacity to each buffer.
+   */
+  static std::unique_ptr<IOBuf> createChain(
+      size_t totalCapacity, uint64_t maxBufCapacity);
+
+  /**
+   * Create a new IOBuf pointing to an existing data buffer.
+   *
+   * The new IOBuffer will assume ownership of the buffer, and free it by
+   * calling the specified FreeFunction when the last IOBuf pointing to this
+   * buffer is destroyed.  The function will be called with a pointer to the
+   * buffer as the first argument, and the supplied userData value as the
+   * second argument.  The free function must never throw exceptions.
+   *
+   * If no FreeFunction is specified, the buffer will be freed using free()
+   * which will result in undefined behavior if the memory was allocated
+   * using 'new'.
+   *
+   * The IOBuf data pointer will initially point to the start of the buffer,
+   *
+   * In the first version of this function, the length of data is unspecified
+   * and is initialized to the capacity of the buffer
+   *
+   * In the second version, the user specifies the valid length of data
+   * in the buffer
+   *
+   * On error, std::bad_alloc will be thrown.  If freeOnError is true (the
+   * default) the buffer will be freed before throwing the error.
+   */
+  static std::unique_ptr<IOBuf> takeOwnership(void* buf, uint64_t capacity,
+                                              FreeFunction freeFn = nullptr,
+                                              void* userData = nullptr,
+                                              bool freeOnError = true) {
+    return takeOwnership(buf, capacity, capacity, freeFn,
+                         userData, freeOnError);
+  }
+  IOBuf(TakeOwnershipOp op, void* buf, uint64_t capacity,
+        FreeFunction freeFn = nullptr, void* userData = nullptr,
+        bool freeOnError = true)
+    : IOBuf(op, buf, capacity, capacity, freeFn, userData, freeOnError) {}
+
+  static std::unique_ptr<IOBuf> takeOwnership(void* buf, uint64_t capacity,
+                                              uint64_t length,
+                                              FreeFunction freeFn = nullptr,
+                                              void* userData = nullptr,
+                                              bool freeOnError = true);
+  IOBuf(TakeOwnershipOp, void* buf, uint64_t capacity, uint64_t length,
+        FreeFunction freeFn = nullptr, void* userData = nullptr,
+        bool freeOnError = true);
+
+  /**
+   * Create a new IOBuf pointing to an existing data buffer made up of
+   * count objects of a given standard-layout type.
+   *
+   * This is dangerous -- it is essentially equivalent to doing
+   * reinterpret_cast<unsigned char*> on your data -- but it's often useful
+   * for serialization / deserialization.
+   *
+   * The new IOBuffer will assume ownership of the buffer, and free it
+   * appropriately (by calling the UniquePtr's custom deleter, or by calling
+   * delete or delete[] appropriately if there is no custom deleter)
+   * when the buffer is destroyed.  The custom deleter, if any, must never
+   * throw exceptions.
+   *
+   * The IOBuf data pointer will initially point to the start of the buffer,
+   * and the length will be the full capacity of the buffer (count *
+   * sizeof(T)).
+   *
+   * On error, std::bad_alloc will be thrown, and the buffer will be freed
+   * before throwing the error.
+   */
+  template <class UniquePtr>
+  static typename std::enable_if<detail::IsUniquePtrToSL<UniquePtr>::value,
+                                 std::unique_ptr<IOBuf>>::type
+  takeOwnership(UniquePtr&& buf, size_t count=1);
+
+  /**
+   * Create a new IOBuf object that points to an existing user-owned buffer.
+   *
+   * This should only be used when the caller knows the lifetime of the IOBuf
+   * object ahead of time and can ensure that all IOBuf objects that will point
+   * to this buffer will be destroyed before the buffer itself is destroyed.
+   *
+   * This buffer will not be freed automatically when the last IOBuf
+   * referencing it is destroyed.  It is the caller's responsibility to free
+   * the buffer after the last IOBuf has been destroyed.
+   *
+   * The IOBuf data pointer will initially point to the start of the buffer,
+   * and the length will be the full capacity of the buffer.
+   *
+   * An IOBuf created using wrapBuffer() will always be reported as shared.
+   * unshare() may be used to create a writable copy of the buffer.
+   *
+   * On error, std::bad_alloc will be thrown.
+   */
+  static std::unique_ptr<IOBuf> wrapBuffer(const void* buf, uint64_t capacity);
+  static std::unique_ptr<IOBuf> wrapBuffer(ByteRange br) {
+    return wrapBuffer(br.data(), br.size());
+  }
+  IOBuf(WrapBufferOp op, const void* buf, uint64_t capacity);
+  IOBuf(WrapBufferOp op, ByteRange br);
+
+  /**
+   * Convenience function to create a new IOBuf object that copies data from a
+   * user-supplied buffer, optionally allocating a given amount of
+   * headroom and tailroom.
+   */
+  static std::unique_ptr<IOBuf> copyBuffer(const void* buf, uint64_t size,
+                                           uint64_t headroom=0,
+                                           uint64_t minTailroom=0);
+  static std::unique_ptr<IOBuf> copyBuffer(ByteRange br,
+                                           uint64_t headroom=0,
+                                           uint64_t minTailroom=0) {
+    return copyBuffer(br.data(), br.size(), headroom, minTailroom);
+  }
+  IOBuf(CopyBufferOp op, const void* buf, uint64_t size,
+        uint64_t headroom=0, uint64_t minTailroom=0);
+  IOBuf(CopyBufferOp op, ByteRange br,
+        uint64_t headroom=0, uint64_t minTailroom=0);
+
+  /**
+   * Convenience function to create a new IOBuf object that copies data from a
+   * user-supplied string, optionally allocating a given amount of
+   * headroom and tailroom.
+   *
+   * Beware when attempting to invoke this function with a constant string
+   * literal and a headroom argument: you will likely end up invoking the
+   * version of copyBuffer() above.  IOBuf::copyBuffer("hello", 3) will treat
+   * the first argument as a const void*, and will invoke the version of
+   * copyBuffer() above, with the size argument of 3.
+   */
+  static std::unique_ptr<IOBuf> copyBuffer(const std::string& buf,
+                                           uint64_t headroom=0,
+                                           uint64_t minTailroom=0);
+  IOBuf(CopyBufferOp op, const std::string& buf,
+        uint64_t headroom=0, uint64_t minTailroom=0)
+    : IOBuf(op, buf.data(), buf.size(), headroom, minTailroom) {}
+
+  /**
+   * A version of copyBuffer() that returns a null pointer if the input string
+   * is empty.
+   */
+  static std::unique_ptr<IOBuf> maybeCopyBuffer(const std::string& buf,
+                                                uint64_t headroom=0,
+                                                uint64_t minTailroom=0);
+
+  /**
+   * Convenience function to free a chain of IOBufs held by a unique_ptr.
+   */
+  static void destroy(std::unique_ptr<IOBuf>&& data) {
+    auto destroyer = std::move(data);
+  }
+
+  /**
+   * Destroy this IOBuf.
+   *
+   * Deleting an IOBuf will automatically destroy all IOBufs in the chain.
+   * (See the comments above regarding the ownership model of IOBuf chains.
+   * All subsequent IOBufs in the chain are considered to be owned by the head
+   * of the chain.  Users should only explicitly delete the head of a chain.)
+   *
+   * When each individual IOBuf is destroyed, it will release its reference
+   * count on the underlying buffer.  If it was the last user of the buffer,
+   * the buffer will be freed.
+   */
+  ~IOBuf();
+
+  /**
+   * Check whether the chain is empty (i.e., whether the IOBufs in the
+   * chain have a total data length of zero).
+   *
+   * This method is semantically equivalent to
+   *   i->computeChainDataLength()==0
+   * but may run faster because it can short-circuit as soon as it
+   * encounters a buffer with length()!=0
+   */
+  bool empty() const;
+
+  /**
+   * Get the pointer to the start of the data.
+   */
+  const uint8_t* data() const {
+    return data_;
+  }
+
+  /**
+   * Get a writable pointer to the start of the data.
+   *
+   * The caller is responsible for calling unshare() first to ensure that it is
+   * actually safe to write to the buffer.
+   */
+  uint8_t* writableData() {
+    return data_;
+  }
+
+  /**
+   * Get the pointer to the end of the data.
+   */
+  const uint8_t* tail() const {
+    return data_ + length_;
+  }
+
+  /**
+   * Get a writable pointer to the end of the data.
+   *
+   * The caller is responsible for calling unshare() first to ensure that it is
+   * actually safe to write to the buffer.
+   */
+  uint8_t* writableTail() {
+    return data_ + length_;
+  }
+
+  /**
+   * Get the data length.
+   */
+  uint64_t length() const {
+    return length_;
+  }
+
+  /**
+   * Get the amount of head room.
+   *
+   * Returns the number of bytes in the buffer before the start of the data.
+   */
+  uint64_t headroom() const {
+    return data_ - buffer();
+  }
+
+  /**
+   * Get the amount of tail room.
+   *
+   * Returns the number of bytes in the buffer after the end of the data.
+   */
+  uint64_t tailroom() const {
+    return bufferEnd() - tail();
+  }
+
+  /**
+   * Get the pointer to the start of the buffer.
+   *
+   * Note that this is the pointer to the very beginning of the usable buffer,
+   * not the start of valid data within the buffer.  Use the data() method to
+   * get a pointer to the start of the data within the buffer.
+   */
+  const uint8_t* buffer() const {
+    return buf_;
+  }
+
+  /**
+   * Get a writable pointer to the start of the buffer.
+   *
+   * The caller is responsible for calling unshare() first to ensure that it is
+   * actually safe to write to the buffer.
+   */
+  uint8_t* writableBuffer() {
+    return buf_;
+  }
+
+  /**
+   * Get the pointer to the end of the buffer.
+   *
+   * Note that this is the pointer to the very end of the usable buffer,
+   * not the end of valid data within the buffer.  Use the tail() method to
+   * get a pointer to the end of the data within the buffer.
+   */
+  const uint8_t* bufferEnd() const {
+    return buf_ + capacity_;
+  }
+
+  /**
+   * Get the total size of the buffer.
+   *
+   * This returns the total usable length of the buffer.  Use the length()
+   * method to get the length of the actual valid data in this IOBuf.
+   */
+  uint64_t capacity() const {
+    return capacity_;
+  }
+
+  /**
+   * Get a pointer to the next IOBuf in this chain.
+   */
+  IOBuf* next() {
+    return next_;
+  }
+  const IOBuf* next() const {
+    return next_;
+  }
+
+  /**
+   * Get a pointer to the previous IOBuf in this chain.
+   */
+  IOBuf* prev() {
+    return prev_;
+  }
+  const IOBuf* prev() const {
+    return prev_;
+  }
+
+  /**
+   * Shift the data forwards in the buffer.
+   *
+   * This shifts the data pointer forwards in the buffer to increase the
+   * headroom.  This is commonly used to increase the headroom in a newly
+   * allocated buffer.
+   *
+   * The caller is responsible for ensuring that there is sufficient
+   * tailroom in the buffer before calling advance().
+   *
+   * If there is a non-zero data length, advance() will use memmove() to shift
+   * the data forwards in the buffer.  In this case, the caller is responsible
+   * for making sure the buffer is unshared, so it will not affect other IOBufs
+   * that may be sharing the same underlying buffer.
+   */
+  void advance(uint64_t amount) {
+    // In debug builds, assert if there is a problem.
+    assert(amount <= tailroom());
+
+    if (length_ > 0) {
+      memmove(data_ + amount, data_, length_);
+    }
+    data_ += amount;
+  }
+
+  /**
+   * Shift the data backwards in the buffer.
+   *
+   * The caller is responsible for ensuring that there is sufficient headroom
+   * in the buffer before calling retreat().
+   *
+   * If there is a non-zero data length, retreat() will use memmove() to shift
+   * the data backwards in the buffer.  In this case, the caller is responsible
+   * for making sure the buffer is unshared, so it will not affect other IOBufs
+   * that may be sharing the same underlying buffer.
+   */
+  void retreat(uint64_t amount) {
+    // In debug builds, assert if there is a problem.
+    assert(amount <= headroom());
+
+    if (length_ > 0) {
+      memmove(data_ - amount, data_, length_);
+    }
+    data_ -= amount;
+  }
+
+  /**
+   * Adjust the data pointer to include more valid data at the beginning.
+   *
+   * This moves the data pointer backwards to include more of the available
+   * buffer.  The caller is responsible for ensuring that there is sufficient
+   * headroom for the new data.  The caller is also responsible for populating
+   * this section with valid data.
+   *
+   * This does not modify any actual data in the buffer.
+   */
+  void prepend(uint64_t amount) {
+    DCHECK_LE(amount, headroom());
+    data_ -= amount;
+    length_ += amount;
+  }
+
+  /**
+   * Adjust the tail pointer to include more valid data at the end.
+   *
+   * This moves the tail pointer forwards to include more of the available
+   * buffer.  The caller is responsible for ensuring that there is sufficient
+   * tailroom for the new data.  The caller is also responsible for populating
+   * this section with valid data.
+   *
+   * This does not modify any actual data in the buffer.
+   */
+  void append(uint64_t amount) {
+    DCHECK_LE(amount, tailroom());
+    length_ += amount;
+  }
+
+  /**
+   * Adjust the data pointer forwards to include less valid data.
+   *
+   * This moves the data pointer forwards so that the first amount bytes are no
+   * longer considered valid data.  The caller is responsible for ensuring that
+   * amount is less than or equal to the actual data length.
+   *
+   * This does not modify any actual data in the buffer.
+   */
+  void trimStart(uint64_t amount) {
+    DCHECK_LE(amount, length_);
+    data_ += amount;
+    length_ -= amount;
+  }
+
+  /**
+   * Adjust the tail pointer backwards to include less valid data.
+   *
+   * This moves the tail pointer backwards so that the last amount bytes are no
+   * longer considered valid data.  The caller is responsible for ensuring that
+   * amount is less than or equal to the actual data length.
+   *
+   * This does not modify any actual data in the buffer.
+   */
+  void trimEnd(uint64_t amount) {
+    DCHECK_LE(amount, length_);
+    length_ -= amount;
+  }
+
+  /**
+   * Clear the buffer.
+   *
+   * Postcondition: headroom() == 0, length() == 0, tailroom() == capacity()
+   */
+  void clear() {
+    data_ = writableBuffer();
+    length_ = 0;
+  }
+
+  /**
+   * Ensure that this buffer has at least minHeadroom headroom bytes and at
+   * least minTailroom tailroom bytes.  The buffer must be writable
+   * (you must call unshare() before this, if necessary).
+   *
+   * Postcondition: headroom() >= minHeadroom, tailroom() >= minTailroom,
+   * the data (between data() and data() + length()) is preserved.
+   */
+  void reserve(uint64_t minHeadroom, uint64_t minTailroom) {
+    // Maybe we don't need to do anything.
+    if (headroom() >= minHeadroom && tailroom() >= minTailroom) {
+      return;
+    }
+    // If the buffer is empty but we have enough total room (head + tail),
+    // move the data_ pointer around.
+    if (length() == 0 &&
+        headroom() + tailroom() >= minHeadroom + minTailroom) {
+      data_ = writableBuffer() + minHeadroom;
+      return;
+    }
+    // Bah, we have to do actual work.
+    reserveSlow(minHeadroom, minTailroom);
+  }
+
+  /**
+   * Return true if this IOBuf is part of a chain of multiple IOBufs, or false
+   * if this is the only IOBuf in its chain.
+   */
+  bool isChained() const {
+    assert((next_ == this) == (prev_ == this));
+    return next_ != this;
+  }
+
+  /**
+   * Get the number of IOBufs in this chain.
+   *
+   * Beware that this method has to walk the entire chain.
+   * Use isChained() if you just want to check if this IOBuf is part of a chain
+   * or not.
+   */
+  size_t countChainElements() const;
+
+  /**
+   * Get the length of all the data in this IOBuf chain.
+   *
+   * Beware that this method has to walk the entire chain.
+   */
+  uint64_t computeChainDataLength() const;
+
+  /**
+   * Insert another IOBuf chain immediately before this IOBuf.
+   *
+   * For example, if there are two IOBuf chains (A, B, C) and (D, E, F),
+   * and B->prependChain(D) is called, the (D, E, F) chain will be subsumed
+   * and become part of the chain starting at A, which will now look like
+   * (A, D, E, F, B, C)
+   *
+   * Note that since IOBuf chains are circular, head->prependChain(other) can
+   * be used to append the other chain at the very end of the chain pointed to
+   * by head.  For example, if there are two IOBuf chains (A, B, C) and
+   * (D, E, F), and A->prependChain(D) is called, the chain starting at A will
+   * now consist of (A, B, C, D, E, F)
+   *
+   * The elements in the specified IOBuf chain will become part of this chain,
+   * and will be owned by the head of this chain.  When this chain is
+   * destroyed, all elements in the supplied chain will also be destroyed.
+   *
+   * For this reason, appendChain() only accepts an rvalue-reference to a
+   * unique_ptr(), to make it clear that it is taking ownership of the supplied
+   * chain.  If you have a raw pointer, you can pass in a new temporary
+   * unique_ptr around the raw pointer.  If you have an existing,
+   * non-temporary unique_ptr, you must call std::move(ptr) to make it clear
+   * that you are destroying the original pointer.
+   */
+  void prependChain(std::unique_ptr<IOBuf>&& iobuf);
+
+  /**
+   * Append another IOBuf chain immediately after this IOBuf.
+   *
+   * For example, if there are two IOBuf chains (A, B, C) and (D, E, F),
+   * and B->appendChain(D) is called, the (D, E, F) chain will be subsumed
+   * and become part of the chain starting at A, which will now look like
+   * (A, B, D, E, F, C)
+   *
+   * The elements in the specified IOBuf chain will become part of this chain,
+   * and will be owned by the head of this chain.  When this chain is
+   * destroyed, all elements in the supplied chain will also be destroyed.
+   *
+   * For this reason, appendChain() only accepts an rvalue-reference to a
+   * unique_ptr(), to make it clear that it is taking ownership of the supplied
+   * chain.  If you have a raw pointer, you can pass in a new temporary
+   * unique_ptr around the raw pointer.  If you have an existing,
+   * non-temporary unique_ptr, you must call std::move(ptr) to make it clear
+   * that you are destroying the original pointer.
+   */
+  void appendChain(std::unique_ptr<IOBuf>&& iobuf) {
+    // Just use prependChain() on the next element in our chain
+    next_->prependChain(std::move(iobuf));
+  }
+
+  /**
+   * Remove this IOBuf from its current chain.
+   *
+   * Since ownership of all elements an IOBuf chain is normally maintained by
+   * the head of the chain, unlink() transfers ownership of this IOBuf from the
+   * chain and gives it to the caller.  A new unique_ptr to the IOBuf is
+   * returned to the caller.  The caller must store the returned unique_ptr (or
+   * call release() on it) to take ownership, otherwise the IOBuf will be
+   * immediately destroyed.
+   *
+   * Since unlink transfers ownership of the IOBuf to the caller, be careful
+   * not to call unlink() on the head of a chain if you already maintain
+   * ownership on the head of the chain via other means.  The pop() method
+   * is a better choice for that situation.
+   */
+  std::unique_ptr<IOBuf> unlink() {
+    next_->prev_ = prev_;
+    prev_->next_ = next_;
+    prev_ = this;
+    next_ = this;
+    return std::unique_ptr<IOBuf>(this);
+  }
+
+  /**
+   * Remove this IOBuf from its current chain and return a unique_ptr to
+   * the IOBuf that formerly followed it in the chain.
+   */
+  std::unique_ptr<IOBuf> pop() {
+    IOBuf *next = next_;
+    next_->prev_ = prev_;
+    prev_->next_ = next_;
+    prev_ = this;
+    next_ = this;
+    return std::unique_ptr<IOBuf>((next == this) ? nullptr : next);
+  }
+
+  /**
+   * Remove a subchain from this chain.
+   *
+   * Remove the subchain starting at head and ending at tail from this chain.
+   *
+   * Returns a unique_ptr pointing to head.  (In other words, ownership of the
+   * head of the subchain is transferred to the caller.)  If the caller ignores
+   * the return value and lets the unique_ptr be destroyed, the subchain will
+   * be immediately destroyed.
+   *
+   * The subchain referenced by the specified head and tail must be part of the
+   * same chain as the current IOBuf, but must not contain the current IOBuf.
+   * However, the specified head and tail may be equal to each other (i.e.,
+   * they may be a subchain of length 1).
+   */
+  std::unique_ptr<IOBuf> separateChain(IOBuf* head, IOBuf* tail) {
+    assert(head != this);
+    assert(tail != this);
+
+    head->prev_->next_ = tail->next_;
+    tail->next_->prev_ = head->prev_;
+
+    head->prev_ = tail;
+    tail->next_ = head;
+
+    return std::unique_ptr<IOBuf>(head);
+  }
+
+  /**
+   * Return true if at least one of the IOBufs in this chain are shared,
+   * or false if all of the IOBufs point to unique buffers.
+   *
+   * Use isSharedOne() to only check this IOBuf rather than the entire chain.
+   */
+  bool isShared() const {
+    const IOBuf* current = this;
+    while (true) {
+      if (current->isSharedOne()) {
+        return true;
+      }
+      current = current->next_;
+      if (current == this) {
+        return false;
+      }
+    }
+  }
+
+  /**
+   * Return true if all IOBufs in this chain are managed by the usual
+   * refcounting mechanism (and so the lifetime of the underlying memory
+   * can be extended by clone()).
+   */
+  bool isManaged() const {
+    const IOBuf* current = this;
+    while (true) {
+      if (!current->isManagedOne()) {
+        return false;
+      }
+      current = current->next_;
+      if (current == this) {
+        return true;
+      }
+    }
+  }
+
+  /**
+   * Return true if this IOBuf is managed by the usual refcounting mechanism
+   * (and so the lifetime of the underlying memory can be extended by
+   * cloneOne()).
+   */
+  bool isManagedOne() const {
+    return sharedInfo();
+  }
+
+  /**
+   * Return true if other IOBufs are also pointing to the buffer used by this
+   * IOBuf, and false otherwise.
+   *
+   * If this IOBuf points at a buffer owned by another (non-IOBuf) part of the
+   * code (i.e., if the IOBuf was created using wrapBuffer(), or was cloned
+   * from such an IOBuf), it is always considered shared.
+   *
+   * This only checks the current IOBuf, and not other IOBufs in the chain.
+   */
+  bool isSharedOne() const {
+    // If this is a user-owned buffer, it is always considered shared
+    if (UNLIKELY(!sharedInfo())) {
+      return true;
+    }
+
+    if (LIKELY(!(flags() & kFlagMaybeShared))) {
+      return false;
+    }
+
+    // kFlagMaybeShared is set, so we need to check the reference count.
+    // (Checking the reference count requires an atomic operation, which is why
+    // we prefer to only check kFlagMaybeShared if possible.)
+    bool shared = sharedInfo()->refcount.load(std::memory_order_acquire) > 1;
+    if (!shared) {
+      // we're the last one left
+      clearFlags(kFlagMaybeShared);
+    }
+    return shared;
+  }
+
+  /**
+   * Ensure that this IOBuf has a unique buffer that is not shared by other
+   * IOBufs.
+   *
+   * unshare() operates on an entire chain of IOBuf objects.  If the chain is
+   * shared, it may also coalesce the chain when making it unique.  If the
+   * chain is coalesced, subsequent IOBuf objects in the current chain will be
+   * automatically deleted.
+   *
+   * Note that buffers owned by other (non-IOBuf) users are automatically
+   * considered shared.
+   *
+   * Throws std::bad_alloc on error.  On error the IOBuf chain will be
+   * unmodified.
+   *
+   * Currently unshare may also throw std::overflow_error if it tries to
+   * coalesce.  (TODO: In the future it would be nice if unshare() were smart
+   * enough not to coalesce the entire buffer if the data is too large.
+   * However, in practice this seems unlikely to become an issue.)
+   */
+  void unshare() {
+    if (isChained()) {
+      unshareChained();
+    } else {
+      unshareOne();
+    }
+  }
+
+  /**
+   * Ensure that this IOBuf has a unique buffer that is not shared by other
+   * IOBufs.
+   *
+   * unshareOne() operates on a single IOBuf object.  This IOBuf will have a
+   * unique buffer after unshareOne() returns, but other IOBufs in the chain
+   * may still be shared after unshareOne() returns.
+   *
+   * Throws std::bad_alloc on error.  On error the IOBuf will be unmodified.
+   */
+  void unshareOne() {
+    if (isSharedOne()) {
+      unshareOneSlow();
+    }
+  }
+
+  /**
+   * Ensure that the memory that IOBufs in this chain refer to will continue to
+   * be allocated for as long as the IOBufs of the chain (or any clone()s
+   * created from this point onwards) is alive.
+   *
+   * This only has an effect for user-owned buffers (created with the
+   * WRAP_BUFFER constructor or wrapBuffer factory function), in which case
+   * those buffers are unshared.
+   */
+  void makeManaged() {
+    if (isChained()) {
+      makeManagedChained();
+    } else {
+      makeManagedOne();
+    }
+  }
+
+  /**
+   * Ensure that the memory that this IOBuf refers to will continue to be
+   * allocated for as long as this IOBuf (or any clone()s created from this
+   * point onwards) is alive.
+   *
+   * This only has an effect for user-owned buffers (created with the
+   * WRAP_BUFFER constructor or wrapBuffer factory function), in which case
+   * those buffers are unshared.
+   */
+  void makeManagedOne() {
+    if (!isManagedOne()) {
+      // We can call the internal function directly; unmanaged implies shared.
+      unshareOneSlow();
+    }
+  }
+
+  /**
+   * Coalesce this IOBuf chain into a single buffer.
+   *
+   * This method moves all of the data in this IOBuf chain into a single
+   * contiguous buffer, if it is not already in one buffer.  After coalesce()
+   * returns, this IOBuf will be a chain of length one.  Other IOBufs in the
+   * chain will be automatically deleted.
+   *
+   * After coalescing, the IOBuf will have at least as much headroom as the
+   * first IOBuf in the chain, and at least as much tailroom as the last IOBuf
+   * in the chain.
+   *
+   * Throws std::bad_alloc on error.  On error the IOBuf chain will be
+   * unmodified.
+   *
+   * Returns ByteRange that points to the data IOBuf stores.
+   */
+  ByteRange coalesce() {
+    if (isChained()) {
+      coalesceSlow();
+    }
+    return ByteRange(data_, length_);
+  }
+
+  /**
+   * Ensure that this chain has at least maxLength bytes available as a
+   * contiguous memory range.
+   *
+   * This method coalesces whole buffers in the chain into this buffer as
+   * necessary until this buffer's length() is at least maxLength.
+   *
+   * After coalescing, the IOBuf will have at least as much headroom as the
+   * first IOBuf in the chain, and at least as much tailroom as the last IOBuf
+   * that was coalesced.
+   *
+   * Throws std::bad_alloc or std::overflow_error on error.  On error the IOBuf
+   * chain will be unmodified.  Throws std::overflow_error if maxLength is
+   * longer than the total chain length.
+   *
+   * Upon return, either enough of the chain was coalesced into a contiguous
+   * region, or the entire chain was coalesced.  That is,
+   * length() >= maxLength || !isChained() is true.
+   */
+  void gather(uint64_t maxLength) {
+    if (!isChained() || length_ >= maxLength) {
+      return;
+    }
+    coalesceSlow(maxLength);
+  }
+
+  /**
+   * Return a new IOBuf chain sharing the same data as this chain.
+   *
+   * The new IOBuf chain will normally point to the same underlying data
+   * buffers as the original chain.  (The one exception to this is if some of
+   * the IOBufs in this chain contain small internal data buffers which cannot
+   * be shared.)
+   */
+  std::unique_ptr<IOBuf> clone() const;
+
+  /**
+   * Return a new IOBuf with the same data as this IOBuf.
+   *
+   * The new IOBuf returned will not be part of a chain (even if this IOBuf is
+   * part of a larger chain).
+   */
+  std::unique_ptr<IOBuf> cloneOne() const;
+
+  /**
+   * Similar to Clone(). But use other as the head node. Other nodes in the
+   * chain (if any) will be allocted on heap.
+   */
+  void cloneInto(IOBuf& other) const;
+
+  /**
+   * Similar to CloneOne(). But to fill an existing IOBuf instead of a new
+   * IOBuf.
+   */
+  void cloneOneInto(IOBuf& other) const;
+
+  /**
+   * Return an iovector suitable for e.g. writev()
+   *
+   *   auto iov = buf->getIov();
+   *   auto xfer = writev(fd, iov.data(), iov.size());
+   *
+   * Naturally, the returned iovector is invalid if you modify the buffer
+   * chain.
+   */
+  folly::fbvector<struct iovec> getIov() const;
+
+  /**
+   * Update an existing iovec array with the IOBuf data.
+   *
+   * New iovecs will be appended to the existing vector; anything already
+   * present in the vector will be left unchanged.
+   *
+   * Naturally, the returned iovec data will be invalid if you modify the
+   * buffer chain.
+   */
+  void appendToIov(folly::fbvector<struct iovec>* iov) const;
+
+  /**
+   * Fill an iovec array with the IOBuf data.
+   *
+   * Returns the number of iovec filled. If there are more buffer than
+   * iovec, returns 0. This version is suitable to use with stack iovec
+   * arrays.
+   *
+   * Naturally, the filled iovec data will be invalid if you modify the
+   * buffer chain.
+   */
+  size_t fillIov(struct iovec* iov, size_t len) const;
+
+  /*
+   * Overridden operator new and delete.
+   * These perform specialized memory management to help support
+   * createCombined(), which allocates IOBuf objects together with the buffer
+   * data.
+   */
+  void* operator new(size_t size);
+  void* operator new(size_t size, void* ptr);
+  void operator delete(void* ptr);
+
+  /**
+   * Destructively convert this IOBuf to a fbstring efficiently.
+   * We rely on fbstring's AcquireMallocatedString constructor to
+   * transfer memory.
+   */
+  fbstring moveToFbString();
+
+  /**
+   * Iteration support: a chain of IOBufs may be iterated through using
+   * STL-style iterators over const ByteRanges.  Iterators are only invalidated
+   * if the IOBuf that they currently point to is removed.
+   */
+  Iterator cbegin() const;
+  Iterator cend() const;
+  Iterator begin() const;
+  Iterator end() const;
+
+  /**
+   * Allocate a new null buffer.
+   *
+   * This can be used to allocate an empty IOBuf on the stack.  It will have no
+   * space allocated for it.  This is generally useful only to later use move
+   * assignment to fill out the IOBuf.
+   */
+  IOBuf() noexcept;
+
+  /**
+   * Move constructor and assignment operator.
+   *
+   * In general, you should only ever move the head of an IOBuf chain.
+   * Internal nodes in an IOBuf chain are owned by the head of the chain, and
+   * should not be moved from.  (Technically, nothing prevents you from moving
+   * a non-head node, but the moved-to node will replace the moved-from node in
+   * the chain.  This has implications for ownership, since non-head nodes are
+   * owned by the chain head.  You are then responsible for relinquishing
+   * ownership of the moved-to node, and manually deleting the moved-from
+   * node.)
+   *
+   * With the move assignment operator, the destination of the move should be
+   * the head of an IOBuf chain or a solitary IOBuf not part of a chain.  If
+   * the move destination is part of a chain, all other IOBufs in the chain
+   * will be deleted.
+   */
+  IOBuf(IOBuf&& other) noexcept;
+  IOBuf& operator=(IOBuf&& other) noexcept;
+
+  IOBuf(const IOBuf& other);
+  IOBuf& operator=(const IOBuf& other);
+
+ private:
+  enum FlagsEnum : uintptr_t {
+    // Adding any more flags would not work on 32-bit architectures,
+    // as these flags are stashed in the least significant 2 bits of a
+    // max-align-aligned pointer.
+    kFlagFreeSharedInfo = 0x1,
+    kFlagMaybeShared = 0x2,
+    kFlagMask = kFlagFreeSharedInfo | kFlagMaybeShared
+  };
+
+  struct SharedInfo {
+    SharedInfo();
+    SharedInfo(FreeFunction fn, void* arg);
+
+    // A pointer to a function to call to free the buffer when the refcount
+    // hits 0.  If this is null, free() will be used instead.
+    FreeFunction freeFn;
+    void* userData;
+    std::atomic<uint32_t> refcount;
+  };
+  // Helper structs for use by operator new and delete
+  struct HeapPrefix;
+  struct HeapStorage;
+  struct HeapFullStorage;
+
+  /**
+   * Create a new IOBuf pointing to an external buffer.
+   *
+   * The caller is responsible for holding a reference count for this new
+   * IOBuf.  The IOBuf constructor does not automatically increment the
+   * reference count.
+   */
+  struct InternalConstructor {};  // avoid conflicts
+  IOBuf(InternalConstructor, uintptr_t flagsAndSharedInfo,
+        uint8_t* buf, uint64_t capacity,
+        uint8_t* data, uint64_t length);
+
+  void unshareOneSlow();
+  void unshareChained();
+  void makeManagedChained();
+  void coalesceSlow();
+  void coalesceSlow(size_t maxLength);
+  // newLength must be the entire length of the buffers between this and
+  // end (no truncation)
+  void coalesceAndReallocate(
+      size_t newHeadroom,
+      size_t newLength,
+      IOBuf* end,
+      size_t newTailroom);
+  void coalesceAndReallocate(size_t newLength, IOBuf* end) {
+    coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom());
+  }
+  void decrementRefcount();
+  void reserveSlow(uint64_t minHeadroom, uint64_t minTailroom);
+  void freeExtBuffer();
+
+  static size_t goodExtBufferSize(uint64_t minCapacity);
+  static void initExtBuffer(uint8_t* buf, size_t mallocSize,
+                            SharedInfo** infoReturn,
+                            uint64_t* capacityReturn);
+  static void allocExtBuffer(uint64_t minCapacity,
+                             uint8_t** bufReturn,
+                             SharedInfo** infoReturn,
+                             uint64_t* capacityReturn);
+  static void releaseStorage(HeapStorage* storage, uint16_t freeFlags);
+  static void freeInternalBuf(void* buf, void* userData);
+
+  /*
+   * Member variables
+   */
+
+  /*
+   * Links to the next and the previous IOBuf in this chain.
+   *
+   * The chain is circularly linked (the last element in the chain points back
+   * at the head), and next_ and prev_ can never be null.  If this IOBuf is the
+   * only element in the chain, next_ and prev_ will both point to this.
+   */
+  IOBuf* next_{this};
+  IOBuf* prev_{this};
+
+  /*
+   * A pointer to the start of the data referenced by this IOBuf, and the
+   * length of the data.
+   *
+   * This may refer to any subsection of the actual buffer capacity.
+   */
+  uint8_t* data_{nullptr};
+  uint8_t* buf_{nullptr};
+  uint64_t length_{0};
+  uint64_t capacity_{0};
+
+  // Pack flags in least significant 2 bits, sharedInfo in the rest
+  mutable uintptr_t flagsAndSharedInfo_{0};
+
+  static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags,
+                                                 SharedInfo* info) {
+    uintptr_t uinfo = reinterpret_cast<uintptr_t>(info);
+    DCHECK_EQ(flags & ~kFlagMask, 0);
+    DCHECK_EQ(uinfo & kFlagMask, 0);
+    return flags | uinfo;
+  }
+
+  inline SharedInfo* sharedInfo() const {
+    return reinterpret_cast<SharedInfo*>(flagsAndSharedInfo_ & ~kFlagMask);
+  }
+
+  inline void setSharedInfo(SharedInfo* info) {
+    uintptr_t uinfo = reinterpret_cast<uintptr_t>(info);
+    DCHECK_EQ(uinfo & kFlagMask, 0);
+    flagsAndSharedInfo_ = (flagsAndSharedInfo_ & kFlagMask) | uinfo;
+  }
+
+  inline uintptr_t flags() const {
+    return flagsAndSharedInfo_ & kFlagMask;
+  }
+
+  // flags_ are changed from const methods
+  inline void setFlags(uintptr_t flags) const {
+    DCHECK_EQ(flags & ~kFlagMask, 0);
+    flagsAndSharedInfo_ |= flags;
+  }
+
+  inline void clearFlags(uintptr_t flags) const {
+    DCHECK_EQ(flags & ~kFlagMask, 0);
+    flagsAndSharedInfo_ &= ~flags;
+  }
+
+  inline void setFlagsAndSharedInfo(uintptr_t flags, SharedInfo* info) {
+    flagsAndSharedInfo_ = packFlagsAndSharedInfo(flags, info);
+  }
+
+  struct DeleterBase {
+    virtual ~DeleterBase() { }
+    virtual void dispose(void* p) = 0;
+  };
+
+  template <class UniquePtr>
+  struct UniquePtrDeleter : public DeleterBase {
+    typedef typename UniquePtr::pointer Pointer;
+    typedef typename UniquePtr::deleter_type Deleter;
+
+    explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)){ }
+    void dispose(void* p) {
+      try {
+        deleter_(static_cast<Pointer>(p));
+        delete this;
+      } catch (...) {
+        abort();
+      }
+    }
+
+   private:
+    Deleter deleter_;
+  };
+
+  static void freeUniquePtrBuffer(void* ptr, void* userData) {
+    static_cast<DeleterBase*>(userData)->dispose(ptr);
+  }
+};
+
+/**
+ * Hasher for IOBuf objects. Hashes the entire chain using SpookyHashV2.
+ */
+struct IOBufHash {
+  size_t operator()(const IOBuf& buf) const;
+  size_t operator()(const std::unique_ptr<IOBuf>& buf) const {
+    return buf ? (*this)(*buf) : 0;
+  }
+};
+
+/**
+ * Equality predicate for IOBuf objects. Compares data in the entire chain.
+ */
+struct IOBufEqual {
+  bool operator()(const IOBuf& a, const IOBuf& b) const;
+  bool operator()(const std::unique_ptr<IOBuf>& a,
+                  const std::unique_ptr<IOBuf>& b) const {
+    if (!a && !b) {
+      return true;
+    } else if (!a || !b) {
+      return false;
+    } else {
+      return (*this)(*a, *b);
+    }
+  }
+};
+
+template <class UniquePtr>
+typename std::enable_if<detail::IsUniquePtrToSL<UniquePtr>::value,
+                        std::unique_ptr<IOBuf>>::type
+IOBuf::takeOwnership(UniquePtr&& buf, size_t count) {
+  size_t size = count * sizeof(typename UniquePtr::element_type);
+  auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter());
+  return takeOwnership(buf.release(),
+                       size,
+                       &IOBuf::freeUniquePtrBuffer,
+                       deleter);
+}
+
+inline std::unique_ptr<IOBuf> IOBuf::copyBuffer(
+    const void* data, uint64_t size, uint64_t headroom,
+    uint64_t minTailroom) {
+  uint64_t capacity = headroom + size + minTailroom;
+  std::unique_ptr<IOBuf> buf = create(capacity);
+  buf->advance(headroom);
+  memcpy(buf->writableData(), data, size);
+  buf->append(size);
+  return buf;
+}
+
+inline std::unique_ptr<IOBuf> IOBuf::copyBuffer(const std::string& buf,
+                                                uint64_t headroom,
+                                                uint64_t minTailroom) {
+  return copyBuffer(buf.data(), buf.size(), headroom, minTailroom);
+}
+
+inline std::unique_ptr<IOBuf> IOBuf::maybeCopyBuffer(const std::string& buf,
+                                                     uint64_t headroom,
+                                                     uint64_t minTailroom) {
+  if (buf.empty()) {
+    return nullptr;
+  }
+  return copyBuffer(buf.data(), buf.size(), headroom, minTailroom);
+}
+
+class IOBuf::Iterator : public boost::iterator_facade<
+    IOBuf::Iterator,  // Derived
+    const ByteRange,  // Value
+    boost::forward_traversal_tag  // Category or traversal
+  > {
+  friend class boost::iterator_core_access;
+ public:
+  // Note that IOBufs are stored as a circular list without a guard node,
+  // so pos == end is ambiguous (it may mean "begin" or "end").  To solve
+  // the ambiguity (at the cost of one extra comparison in the "increment"
+  // code path), we define end iterators as having pos_ == end_ == nullptr
+  // and we only allow forward iteration.
+  explicit Iterator(const IOBuf* pos, const IOBuf* end)
+    : pos_(pos),
+      end_(end) {
+    // Sadly, we must return by const reference, not by value.
+    if (pos_) {
+      setVal();
+    }
+  }
+
+ private:
+  void setVal() {
+    val_ = ByteRange(pos_->data(), pos_->tail());
+  }
+
+  void adjustForEnd() {
+    if (pos_ == end_) {
+      pos_ = end_ = nullptr;
+      val_ = ByteRange();
+    } else {
+      setVal();
+    }
+  }
+
+  const ByteRange& dereference() const {
+    return val_;
+  }
+
+  bool equal(const Iterator& other) const {
+    // We must compare end_ in addition to pos_, because forward traversal
+    // requires that if two iterators are equal (a == b) and dereferenceable,
+    // then ++a == ++b.
+    return pos_ == other.pos_ && end_ == other.end_;
+  }
+
+  void increment() {
+    pos_ = pos_->next();
+    adjustForEnd();
+  }
+
+  const IOBuf* pos_;
+  const IOBuf* end_;
+  ByteRange val_;
+};
+
+inline IOBuf::Iterator IOBuf::begin() const { return cbegin(); }
+inline IOBuf::Iterator IOBuf::end() const { return cend(); }
+
+} // folly
+
+#pragma GCC diagnostic pop
+
+#endif // FOLLY_IO_IOBUF_H_
diff --git a/faux-folly/folly/io/IOBufQueue.cpp b/faux-folly/folly/io/IOBufQueue.cpp
new file mode 100644
index 0000000..37e0635
--- /dev/null
+++ b/faux-folly/folly/io/IOBufQueue.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2015 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/io/IOBufQueue.h>
+
+#include <string.h>
+
+#include <stdexcept>
+
+using std::make_pair;
+using std::pair;
+using std::unique_ptr;
+
+namespace {
+
+using folly::IOBuf;
+
+const size_t MIN_ALLOC_SIZE = 2000;
+const size_t MAX_ALLOC_SIZE = 8000;
+const size_t MAX_PACK_COPY = 4096;
+
+/**
+ * Convenience function to append chain src to chain dst.
+ */
+void
+appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) {
+  if (dst == nullptr) {
+    dst = std::move(src);
+  } else {
+    IOBuf* tail = dst->prev();
+    if (pack) {
+      // Copy up to MAX_PACK_COPY bytes if we can free buffers; this helps
+      // reduce wastage (the tail's tailroom and the head's headroom) when
+      // joining two IOBufQueues together.
+      size_t copyRemaining = MAX_PACK_COPY;
+      uint64_t n;
+      while (src &&
+             (n = src->length()) < copyRemaining &&
+             n < tail->tailroom()) {
+        memcpy(tail->writableTail(), src->data(), n);
+        tail->append(n);
+        copyRemaining -= n;
+        src = src->pop();
+      }
+    }
+    if (src) {
+      tail->appendChain(std::move(src));
+    }
+  }
+}
+
+} // anonymous namespace
+
+namespace folly {
+
+IOBufQueue::IOBufQueue(const Options& options)
+  : options_(options),
+    chainLength_(0) {
+}
+
+IOBufQueue::IOBufQueue(IOBufQueue&& other) noexcept
+  : options_(other.options_),
+    chainLength_(other.chainLength_),
+    head_(std::move(other.head_)) {
+  other.chainLength_ = 0;
+}
+
+IOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) {
+  if (&other != this) {
+    options_ = other.options_;
+    chainLength_ = other.chainLength_;
+    head_ = std::move(other.head_);
+    other.chainLength_ = 0;
+  }
+  return *this;
+}
+
+std::pair<void*, uint64_t>
+IOBufQueue::headroom() {
+  if (head_) {
+    return std::make_pair(head_->writableBuffer(), head_->headroom());
+  } else {
+    return std::make_pair(nullptr, 0);
+  }
+}
+
+void
+IOBufQueue::markPrepended(uint64_t n) {
+  if (n == 0) {
+    return;
+  }
+  assert(head_);
+  head_->prepend(n);
+  chainLength_ += n;
+}
+
+void
+IOBufQueue::prepend(const void* buf, uint64_t n) {
+  auto p = headroom();
+  if (n > p.second) {
+    throw std::overflow_error("Not enough room to prepend");
+  }
+  memcpy(static_cast<char*>(p.first) + p.second - n, buf, n);
+  markPrepended(n);
+}
+
+void
+IOBufQueue::append(unique_ptr<IOBuf>&& buf, bool pack) {
+  if (!buf) {
+    return;
+  }
+  if (options_.cacheChainLength) {
+    chainLength_ += buf->computeChainDataLength();
+  }
+  appendToChain(head_, std::move(buf), pack);
+}
+
+void
+IOBufQueue::append(IOBufQueue& other, bool pack) {
+  if (!other.head_) {
+    return;
+  }
+  if (options_.cacheChainLength) {
+    if (other.options_.cacheChainLength) {
+      chainLength_ += other.chainLength_;
+    } else {
+      chainLength_ += other.head_->computeChainDataLength();
+    }
+  }
+  appendToChain(head_, std::move(other.head_), pack);
+  other.chainLength_ = 0;
+}
+
+void
+IOBufQueue::append(const void* buf, size_t len) {
+  auto src = static_cast<const uint8_t*>(buf);
+  while (len != 0) {
+    if ((head_ == nullptr) || head_->prev()->isSharedOne() ||
+        (head_->prev()->tailroom() == 0)) {
+      appendToChain(head_, std::move(
+          IOBuf::create(std::max(MIN_ALLOC_SIZE,
+              std::min(len, MAX_ALLOC_SIZE)))),
+          false);
+    }
+    IOBuf* last = head_->prev();
+    uint64_t copyLen = std::min(len, (size_t)last->tailroom());
+    memcpy(last->writableTail(), src, copyLen);
+    src += copyLen;
+    last->append(copyLen);
+    chainLength_ += copyLen;
+    len -= copyLen;
+  }
+}
+
+void
+IOBufQueue::wrapBuffer(const void* buf, size_t len, uint64_t blockSize) {
+  auto src = static_cast<const uint8_t*>(buf);
+  while (len != 0) {
+    size_t n = std::min(len, size_t(blockSize));
+    append(IOBuf::wrapBuffer(src, n));
+    src += n;
+    len -= n;
+  }
+}
+
+pair<void*,uint64_t>
+IOBufQueue::preallocateSlow(uint64_t min, uint64_t newAllocationSize,
+                            uint64_t max) {
+  // Allocate a new buffer of the requested max size.
+  unique_ptr<IOBuf> newBuf(IOBuf::create(std::max(min, newAllocationSize)));
+  appendToChain(head_, std::move(newBuf), false);
+  IOBuf* last = head_->prev();
+  return make_pair(last->writableTail(),
+                   std::min(max, last->tailroom()));
+}
+
+unique_ptr<IOBuf>
+IOBufQueue::split(size_t n) {
+  unique_ptr<IOBuf> result;
+  while (n != 0) {
+    if (head_ == nullptr) {
+      throw std::underflow_error(
+          "Attempt to remove more bytes than are present in IOBufQueue");
+    } else if (head_->length() <= n) {
+      n -= head_->length();
+      chainLength_ -= head_->length();
+      unique_ptr<IOBuf> remainder = head_->pop();
+      appendToChain(result, std::move(head_), false);
+      head_ = std::move(remainder);
+    } else {
+      unique_ptr<IOBuf> clone = head_->cloneOne();
+      clone->trimEnd(clone->length() - n);
+      appendToChain(result, std::move(clone), false);
+      head_->trimStart(n);
+      chainLength_ -= n;
+      break;
+    }
+  }
+  return std::move(result);
+}
+
+void IOBufQueue::trimStart(size_t amount) {
+  while (amount > 0) {
+    if (!head_) {
+      throw std::underflow_error(
+        "Attempt to trim more bytes than are present in IOBufQueue");
+    }
+    if (head_->length() > amount) {
+      head_->trimStart(amount);
+      chainLength_ -= amount;
+      break;
+    }
+    amount -= head_->length();
+    chainLength_ -= head_->length();
+    head_ = head_->pop();
+  }
+}
+
+void IOBufQueue::trimEnd(size_t amount) {
+  while (amount > 0) {
+    if (!head_) {
+      throw std::underflow_error(
+        "Attempt to trim more bytes than are present in IOBufQueue");
+    }
+    if (head_->prev()->length() > amount) {
+      head_->prev()->trimEnd(amount);
+      chainLength_ -= amount;
+      break;
+    }
+    amount -= head_->prev()->length();
+    chainLength_ -= head_->prev()->length();
+
+    if (head_->isChained()) {
+      head_->prev()->unlink();
+    } else {
+      head_.reset();
+    }
+  }
+}
+
+std::unique_ptr<folly::IOBuf> IOBufQueue::pop_front() {
+  if (!head_) {
+    return nullptr;
+  }
+  chainLength_ -= head_->length();
+  std::unique_ptr<folly::IOBuf> retBuf = std::move(head_);
+  head_ = retBuf->pop();
+  return retBuf;
+}
+
+void IOBufQueue::clear() {
+  if (!head_) {
+    return;
+  }
+  IOBuf* buf = head_.get();
+  do {
+    buf->clear();
+    buf = buf->next();
+  } while (buf != head_.get());
+  chainLength_ = 0;
+}
+
+void IOBufQueue::appendToString(std::string& out) const {
+  if (!head_) {
+    return;
+  }
+  auto len =
+    options_.cacheChainLength ? chainLength_ : head_->computeChainDataLength();
+  out.reserve(out.size() + len);
+
+  for (auto range : *head_) {
+    out.append(reinterpret_cast<const char*>(range.data()), range.size());
+  }
+}
+
+} // folly
diff --git a/faux-folly/folly/io/IOBufQueue.h b/faux-folly/folly/io/IOBufQueue.h
new file mode 100644
index 0000000..554a0a3
--- /dev/null
+++ b/faux-folly/folly/io/IOBufQueue.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_IO_IOBUF_QUEUE_H
+#define FOLLY_IO_IOBUF_QUEUE_H
+
+#include <folly/io/IOBuf.h>
+
+#include <stdexcept>
+#include <string>
+
+namespace folly {
+
+/**
+ * An IOBufQueue encapsulates a chain of IOBufs and provides
+ * convenience functions to append data to the back of the chain
+ * and remove data from the front.
+ *
+ * You may also prepend data into the headroom of the first buffer in the
+ * chain, if any.
+ */
+class IOBufQueue {
+ public:
+  struct Options {
+    Options() : cacheChainLength(false) { }
+    bool cacheChainLength;
+  };
+
+  /**
+   * Commonly used Options, currently the only possible value other than
+   * the default.
+   */
+  static Options cacheChainLength() {
+    Options options;
+    options.cacheChainLength = true;
+    return options;
+  }
+
+  explicit IOBufQueue(const Options& options = Options());
+
+  /**
+   * Return a space to prepend bytes and the amount of headroom available.
+   */
+  std::pair<void*, uint64_t> headroom();
+
+  /**
+   * Indicate that n bytes from the headroom have been used.
+   */
+  void markPrepended(uint64_t n);
+
+  /**
+   * Prepend an existing range; throws std::overflow_error if not enough
+   * room.
+   */
+  void prepend(const void* buf, uint64_t n);
+
+  /**
+   * Add a buffer or buffer chain to the end of this queue. The
+   * queue takes ownership of buf.
+   *
+   * If pack is true, we try to reduce wastage at the end of this queue
+   * by copying some data from the first buffers in the buf chain (and
+   * releasing the buffers), if possible.  If pack is false, we leave
+   * the chain topology unchanged.
+   */
+  void append(std::unique_ptr<folly::IOBuf>&& buf,
+              bool pack=false);
+
+  /**
+   * Add a queue to the end of this queue. The queue takes ownership of
+   * all buffers from the other queue.
+   */
+  void append(IOBufQueue& other, bool pack=false);
+  void append(IOBufQueue&& other, bool pack=false) {
+    append(other, pack);  // call lvalue reference overload, above
+  }
+
+  /**
+   * Copy len bytes, starting at buf, to the end of this queue.
+   * The caller retains ownership of the source data.
+   */
+  void append(const void* buf, size_t len);
+
+  /**
+   * Copy a string to the end of this queue.
+   * The caller retains ownership of the source data.
+   */
+  void append(StringPiece sp) {
+    append(sp.data(), sp.size());
+  }
+
+  /**
+   * Append a chain of IOBuf objects that point to consecutive regions
+   * within buf.
+   *
+   * Just like IOBuf::wrapBuffer, this should only be used when the caller
+   * knows ahead of time and can ensure that all IOBuf objects that will point
+   * to this buffer will be destroyed before the buffer itself is destroyed;
+   * all other caveats from wrapBuffer also apply.
+   *
+   * Every buffer except for the last will wrap exactly blockSize bytes.
+   * Importantly, this method may be used to wrap buffers larger than 4GB.
+   */
+  void wrapBuffer(const void* buf, size_t len,
+                  uint64_t blockSize=(1U << 31));  // default block size: 2GB
+
+  /**
+   * Obtain a writable block of contiguous bytes at the end of this
+   * queue, allocating more space if necessary.  The amount of space
+   * reserved will be at least min.  If min contiguous space is not
+   * available at the end of the queue, and IOBuf with size newAllocationSize
+   * is appended to the chain and returned.  The actual available space
+   * may be larger than newAllocationSize, but will be truncated to max,
+   * if specified.
+   *
+   * If the caller subsequently writes anything into the returned space,
+   * it must call the postallocate() method.
+   *
+   * @return The starting address of the block and the length in bytes.
+   *
+   * @note The point of the preallocate()/postallocate() mechanism is
+   *       to support I/O APIs such as Thrift's TAsyncSocket::ReadCallback
+   *       that request a buffer from the application and then, in a later
+   *       callback, tell the application how much of the buffer they've
+   *       filled with data.
+   */
+  std::pair<void*,uint64_t> preallocate(
+    uint64_t min, uint64_t newAllocationSize,
+    uint64_t max = std::numeric_limits<uint64_t>::max()) {
+    auto buf = tailBuf();
+    if (LIKELY(buf && buf->tailroom() >= min)) {
+      return std::make_pair(buf->writableTail(),
+                            std::min(max, buf->tailroom()));
+    }
+
+    return preallocateSlow(min, newAllocationSize, max);
+  }
+
+  /**
+   * Tell the queue that the caller has written data into the first n
+   * bytes provided by the previous preallocate() call.
+   *
+   * @note n should be less than or equal to the size returned by
+   *       preallocate().  If n is zero, the caller may skip the call
+   *       to postallocate().  If n is nonzero, the caller must not
+   *       invoke any other non-const methods on this IOBufQueue between
+   *       the call to preallocate and the call to postallocate().
+   */
+  void postallocate(uint64_t n) {
+    head_->prev()->append(n);
+    chainLength_ += n;
+  }
+
+  /**
+   * Obtain a writable block of n contiguous bytes, allocating more space
+   * if necessary, and mark it as used.  The caller can fill it later.
+   */
+  void* allocate(uint64_t n) {
+    void* p = preallocate(n, n).first;
+    postallocate(n);
+    return p;
+  }
+
+  void* writableTail() const {
+    auto buf = tailBuf();
+    return buf ? buf->writableTail() : nullptr;
+  }
+
+  size_t tailroom() const {
+    auto buf = tailBuf();
+    return buf ? buf->tailroom() : 0;
+  }
+
+  /**
+   * Split off the first n bytes of the queue into a separate IOBuf chain,
+   * and transfer ownership of the new chain to the caller.  The IOBufQueue
+   * retains ownership of everything after the split point.
+   *
+   * @warning If the split point lies in the middle of some IOBuf within
+   *          the chain, this function may, as an implementation detail,
+   *          clone that IOBuf.
+   *
+   * @throws std::underflow_error if n exceeds the number of bytes
+   *         in the queue.
+   */
+  std::unique_ptr<folly::IOBuf> split(size_t n);
+
+  /**
+   * Similar to IOBuf::trimStart, but works on the whole queue.  Will
+   * pop off buffers that have been completely trimmed.
+   */
+  void trimStart(size_t amount);
+
+  /**
+   * Similar to IOBuf::trimEnd, but works on the whole queue.  Will
+   * pop off buffers that have been completely trimmed.
+   */
+  void trimEnd(size_t amount);
+
+  /**
+   * Transfer ownership of the queue's entire IOBuf chain to the caller.
+   */
+  std::unique_ptr<folly::IOBuf> move() {
+    chainLength_ = 0;
+    return std::move(head_);
+  }
+
+  /**
+   * Access
+   */
+  const folly::IOBuf* front() const {
+    return head_.get();
+  }
+
+  /**
+   * returns the first IOBuf in the chain and removes it from the chain
+   *
+   * @return first IOBuf in the chain or nullptr if none.
+   */
+  std::unique_ptr<folly::IOBuf> pop_front();
+
+  /**
+   * Total chain length, only valid if cacheLength was specified in the
+   * constructor.
+   */
+  size_t chainLength() const {
+    if (UNLIKELY(!options_.cacheChainLength)) {
+      throw std::invalid_argument("IOBufQueue: chain length not cached");
+    }
+    return chainLength_;
+  }
+
+  /**
+   * Returns true iff the IOBuf chain length is 0.
+   */
+  bool empty() const {
+    return !head_ || head_->empty();
+  }
+
+  const Options& options() const {
+    return options_;
+  }
+
+  /**
+   * Clear the queue.  Note that this does not release the buffers, it
+   * just sets their length to zero; useful if you want to reuse the
+   * same queue without reallocating.
+   */
+  void clear();
+
+  /**
+   * Append the queue to a std::string. Non-destructive.
+   */
+  void appendToString(std::string& out) const;
+
+  /** Movable */
+  IOBufQueue(IOBufQueue&&) noexcept;
+  IOBufQueue& operator=(IOBufQueue&&);
+
+ private:
+  IOBuf* tailBuf() const {
+    if (UNLIKELY(!head_)) return nullptr;
+    IOBuf* buf = head_->prev();
+    return LIKELY(!buf->isSharedOne()) ? buf : nullptr;
+  }
+  std::pair<void*,uint64_t> preallocateSlow(
+    uint64_t min, uint64_t newAllocationSize, uint64_t max);
+
+  static const size_t kChainLengthNotCached = (size_t)-1;
+  /** Not copyable */
+  IOBufQueue(const IOBufQueue&) = delete;
+  IOBufQueue& operator=(const IOBufQueue&) = delete;
+
+  Options options_;
+
+  // NOTE that chainLength_ is still updated even if !options_.cacheChainLength
+  // because doing it unchecked in postallocate() is faster (no (mis)predicted
+  // branch)
+  size_t chainLength_;
+  /** Everything that has been appended but not yet discarded or moved out */
+  std::unique_ptr<folly::IOBuf> head_;
+};
+
+} // folly
+
+#endif // FOLLY_IO_IOBUF_QUEUE_H
diff --git a/faux-folly/folly/io/async/EventBaseLocal.cpp b/faux-folly/folly/io/async/EventBaseLocal.cpp
new file mode 100644
index 0000000..0f08b63
--- /dev/null
+++ b/faux-folly/folly/io/async/EventBaseLocal.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2015 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/io/async/EventBaseLocal.h>
+#include <atomic>
+#include <thread>
+
+namespace folly { namespace detail {
+
+EventBaseLocalBase::~EventBaseLocalBase() {
+  // There's a race condition if an EventBase and an EventBaseLocal destruct
+  // at the same time (each will lock eventBases_ and localStorageMutex_
+  // in the opposite order), so we dance around it with a loop and try_lock.
+  while (true) {
+    SYNCHRONIZED(eventBases_) {
+      auto it = eventBases_.begin();
+      while (it != eventBases_.end()) {
+        auto evb = *it;
+        if (evb->localStorageMutex_.try_lock()) {
+          evb->localStorage_.erase(key_);
+          evb->localStorageToDtor_.erase(this);
+          it = eventBases_.erase(it);
+          evb->localStorageMutex_.unlock();
+        } else {
+          ++it;
+        }
+      }
+
+      if (eventBases_.empty()) {
+        return;
+      }
+    }
+    std::this_thread::yield(); // let the other thread take the eventBases_ lock
+  }
+}
+
+void* EventBaseLocalBase::getVoid(EventBase& evb) {
+  std::lock_guard<std::mutex> lg(evb.localStorageMutex_);
+  auto it2 = evb.localStorage_.find(key_);
+  if (UNLIKELY(it2 != evb.localStorage_.end())) {
+    return it2->second.get();
+  }
+
+  return nullptr;
+}
+
+void EventBaseLocalBase::erase(EventBase& evb) {
+  std::lock_guard<std::mutex> lg(evb.localStorageMutex_);
+  evb.localStorage_.erase(key_);
+  evb.localStorageToDtor_.erase(this);
+
+  SYNCHRONIZED(eventBases_) {
+    eventBases_.erase(&evb);
+  }
+}
+
+void EventBaseLocalBase::onEventBaseDestruction(EventBase& evb) {
+  SYNCHRONIZED(eventBases_) {
+    eventBases_.erase(&evb);
+  }
+}
+
+void EventBaseLocalBase::setVoid(EventBase& evb, std::shared_ptr<void>&& ptr) {
+  std::lock_guard<std::mutex> lg(evb.localStorageMutex_);
+  setVoidUnlocked(evb, std::move(ptr));
+}
+
+void EventBaseLocalBase::setVoidUnlocked(
+    EventBase& evb, std::shared_ptr<void>&& ptr) {
+
+  auto alreadyExists =
+    evb.localStorage_.find(key_) != evb.localStorage_.end();
+
+  evb.localStorage_.emplace(key_, std::move(ptr));
+
+  if (!alreadyExists) {
+    SYNCHRONIZED(eventBases_) {
+      eventBases_.insert(&evb);
+    }
+    evb.localStorageToDtor_.insert(this);
+  }
+}
+
+std::atomic<uint64_t> EventBaseLocalBase::keyCounter_{0};
+}}
diff --git a/faux-folly/folly/io/async/EventBaseLocal.h b/faux-folly/folly/io/async/EventBaseLocal.h
new file mode 100644
index 0000000..334cb60
--- /dev/null
+++ b/faux-folly/folly/io/async/EventBaseLocal.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 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 <boost/noncopyable.hpp>
+#include <folly/Synchronized.h>
+#include <folly/io/async/EventBase.h>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace folly {
+
+namespace detail {
+
+class EventBaseLocalBase : public EventBaseLocalBaseBase, boost::noncopyable {
+ public:
+  EventBaseLocalBase() {}
+  virtual ~EventBaseLocalBase();
+  void erase(EventBase& evb);
+  void onEventBaseDestruction(EventBase& evb) override;
+
+ protected:
+  void setVoid(EventBase& evb, std::shared_ptr<void>&& ptr);
+  void setVoidUnlocked(EventBase& evb, std::shared_ptr<void>&& ptr);
+  void* getVoid(EventBase& evb);
+
+  folly::Synchronized<std::unordered_set<EventBase*>> eventBases_;
+  static std::atomic<uint64_t> keyCounter_;
+  uint64_t key_{keyCounter_++};
+};
+
+}
+
+/**
+ * A storage abstraction for data that should be tied to an EventBase.
+ *
+ *   struct Foo { Foo(int a, int b); };
+ *   EventBaseLocal<Foo> myFoo;
+ *   ...
+ *   EventBase evb;
+ *   myFoo.set(evb, new Foo(1, 2));
+ *   myFoo.set(evb, 1, 2);
+ *   Foo* foo = myFoo.get(evb);
+ *   myFoo.erase(evb);
+ *   Foo& foo = myFoo.getOrCreate(evb, 1, 2); // ctor
+ *   Foo& foo = myFoo.getOrCreate(evb, 1, 2); // no ctor
+ *   myFoo.erase(evb);
+ *   Foo& foo = myFoo.getOrCreateFn(evb, [] () { return new Foo(3, 4); })
+ *
+ * The objects will be deleted when the EventBaseLocal or the EventBase is
+ * destructed (whichever comes first).  All methods are thread-safe.
+ *
+ * The user is responsible for throwing away invalid references/ptrs returned
+ * by the get() method after set/erase is called.  If shared ownership is
+ * needed, use a EventBaseLocal<shared_ptr<...>>.
+ */
+template<typename T>
+class EventBaseLocal : public detail::EventBaseLocalBase {
+ public:
+  EventBaseLocal(): EventBaseLocalBase() {}
+
+  T* get(EventBase& evb) {
+    return static_cast<T*>(getVoid(evb));
+  }
+
+  void emplace(EventBase& evb, T* ptr) {
+    std::shared_ptr<T> smartPtr(ptr);
+    setVoid(evb, std::move(smartPtr));
+  }
+
+  template<typename... Args>
+  void emplace(EventBase& evb, Args... args) {
+    auto smartPtr = std::make_shared<T>(args...);
+    setVoid(evb, smartPtr);
+  }
+
+  template<typename... Args>
+  T& getOrCreate(EventBase& evb, Args... args) {
+    std::lock_guard<std::mutex> lg(evb.localStorageMutex_);
+
+    auto it2 = evb.localStorage_.find(key_);
+    if (LIKELY(it2 != evb.localStorage_.end())) {
+      return *static_cast<T*>(it2->second.get());
+    } else {
+      auto smartPtr = std::make_shared<T>(args...);
+      auto ptr = smartPtr.get();
+      setVoidUnlocked(evb, std::move(smartPtr));
+      return *ptr;
+    }
+  }
+
+  template <typename Func>
+  T& getOrCreateFn(EventBase& evb, Func& fn) {
+    // If this looks like it's copy/pasted from above, that's because it is.
+    // gcc has a bug (fixed in 4.9) that doesn't allow capturing variadic
+    // params in a lambda.
+    std::lock_guard<std::mutex> lg(evb.localStorageMutex_);
+
+    auto it2 = evb.localStorage_.find(key_);
+    if (LIKELY(it2 != evb.localStorage_.end())) {
+      return *static_cast<T*>(it2->second.get());
+    } else {
+      std::shared_ptr<T> smartPtr(fn());
+      auto ptr = smartPtr.get();
+      setVoidUnlocked(evb, std::move(smartPtr));
+      return *ptr;
+    }
+  }
+};
+
+
+}
diff --git a/faux-folly/folly/io/async/test/EventBaseLocalTest.cpp b/faux-folly/folly/io/async/test/EventBaseLocalTest.cpp
new file mode 100644
index 0000000..46b7769
--- /dev/null
+++ b/faux-folly/folly/io/async/test/EventBaseLocalTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 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.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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/io/async/EventBaseLocal.h>
+#include <folly/io/async/test/Util.h>
+#include <gtest/gtest.h>
+
+struct Foo {
+  Foo(int n, std::function<void()> dtorFn):
+    n(n), dtorFn(std::move(dtorFn)) {}
+  ~Foo() { dtorFn(); }
+
+  int n;
+  std::function<void()> dtorFn;
+};
+
+TEST(EventBaseLocalTest, Basic) {
+  int dtorCnt = 0;
+  folly::EventBase evb1;
+
+  {
+    folly::EventBaseLocal<Foo> foo;
+
+    EXPECT_EQ(foo.get(evb1), nullptr);
+
+    foo.emplace(evb1, new Foo(5, [&] () { ++dtorCnt; }));
+
+    EXPECT_EQ(foo.get(evb1)->n, 5);
+
+    {
+      folly::EventBase evb2;
+      foo.emplace(evb2, new Foo(6, [&] () { ++dtorCnt; }));
+      EXPECT_EQ(foo.get(evb2)->n, 6);
+      foo.erase(evb2);
+      EXPECT_EQ(dtorCnt, 1); // should dtor a Foo when we erase
+      EXPECT_EQ(foo.get(evb2), nullptr);
+      foo.emplace(evb2, 7, [&] () { ++dtorCnt; });
+      EXPECT_EQ(foo.get(evb2)->n, 7);
+    }
+
+    EXPECT_EQ(dtorCnt, 2); // should dtor a Foo when evb2 destructs
+
+  }
+  EXPECT_EQ(dtorCnt, 3); // should dtor a Foo when foo destructs
+}
+
+TEST(EventBaseLocalTest, getOrCreate) {
+  folly::EventBase evb1;
+  folly::EventBaseLocal<int> ints;
+
+  EXPECT_EQ(ints.getOrCreate(evb1), 0);
+  EXPECT_EQ(ints.getOrCreate(evb1, 5), 0);
+
+  folly::EventBase evb2;
+  EXPECT_EQ(ints.getOrCreate(evb2, 5), 5);
+  ints.erase(evb2);
+  auto creator = []() { return new int(4); };
+  EXPECT_EQ(ints.getOrCreateFn(evb2, creator), 4);
+}
diff --git a/faux-folly/folly/io/async/test/MockAsyncUDPSocket.h b/faux-folly/folly/io/async/test/MockAsyncUDPSocket.h
new file mode 100644
index 0000000..80f5ccd
--- /dev/null
+++ b/faux-folly/folly/io/async/test/MockAsyncUDPSocket.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 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 <gmock/gmock.h>
+
+#include <folly/io/async/AsyncUDPSocket.h>
+
+namespace folly { namespace test {
+
+struct MockAsyncUDPSocket : public AsyncUDPSocket {
+  explicit MockAsyncUDPSocket(EventBase* evb) : AsyncUDPSocket(evb) {}
+  virtual ~MockAsyncUDPSocket() {}
+
+  MOCK_CONST_METHOD0(address, const SocketAddress&());
+  MOCK_METHOD1(bind, void(const SocketAddress&));
+  MOCK_METHOD2(setFD, void(int, AsyncUDPSocket::FDOwnership));
+  MOCK_METHOD2(
+   write,
+   ssize_t(const SocketAddress&, const std::unique_ptr<IOBuf>&));
+  MOCK_METHOD3(
+   writev,
+   ssize_t(const SocketAddress&, const struct iovec*, size_t));
+  MOCK_METHOD1(resumeRead, void(ReadCallback*));
+  MOCK_METHOD0(pauseRead, void());
+  MOCK_METHOD0(close, void());
+  MOCK_CONST_METHOD0(getFD, int());
+  MOCK_METHOD1(setReusePort, void(bool));
+  MOCK_METHOD1(setReuseAddr, void(bool));
+};
+
+}}
diff --git a/faux-folly/folly/io/async/test/RequestContextTest.cpp b/faux-folly/folly/io/async/test/RequestContextTest.cpp
new file mode 100644
index 0000000..529a15a
--- /dev/null
+++ b/faux-folly/folly/io/async/test/RequestContextTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <gtest/gtest.h>
+#include <thread>
+
+#include <folly/io/async/EventBase.h>
+#include <folly/io/async/Request.h>
+
+using namespace folly;
+
+class TestData : public RequestData {
+ public:
+  explicit TestData(int data) : data_(data) {}
+  ~TestData() override {}
+  int data_;
+};
+
+TEST(RequestContext, SimpleTest) {
+  EventBase base;
+
+
+  // There should always be a default context with get()
+  EXPECT_TRUE(RequestContext::get() != nullptr);
+
+
+  // but not with saveContext()
+  EXPECT_EQ(RequestContext::saveContext(), nullptr);
+  RequestContext::create();
+  EXPECT_NE(RequestContext::saveContext(), nullptr);
+  RequestContext::create();
+  EXPECT_NE(RequestContext::saveContext(), nullptr);
+
+  EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
+
+  RequestContext::get()->setContextData(
+    "test",
+    std::unique_ptr<TestData>(new TestData(10)));
+  base.runInEventBaseThread([&](){
+      EXPECT_TRUE(RequestContext::get() != nullptr);
+      auto data = dynamic_cast<TestData*>(
+        RequestContext::get()->getContextData("test"))->data_;
+      EXPECT_EQ(10, data);
+      base.terminateLoopSoon();
+    });
+  auto th = std::thread([&](){
+      base.loopForever();
+  });
+  th.join();
+  EXPECT_TRUE(RequestContext::get() != nullptr);
+  auto a = dynamic_cast<TestData*>(
+    RequestContext::get()->getContextData("test"));
+  auto data = a->data_;
+  EXPECT_EQ(10, data);
+
+  RequestContext::setContext(std::shared_ptr<RequestContext>());
+  // There should always be a default context
+  EXPECT_TRUE(nullptr != RequestContext::get());
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  google::InitGoogleLogging(argv[0]);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/m4/ac_cxx_compile_stdcxx_0x.m4 b/faux-folly/folly/m4/ac_cxx_compile_stdcxx_0x.m4
new file mode 100644
index 0000000..5c1a3bd
--- /dev/null
+++ b/faux-folly/folly/m4/ac_cxx_compile_stdcxx_0x.m4
@@ -0,0 +1,110 @@
+# ===========================================================================
+#        http://autoconf-archive.cryp.to/ac_cxx_compile_stdcxx_0x.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AC_CXX_COMPILE_STDCXX_0X
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the C++0x
+#   standard.
+#
+# LAST MODIFICATION
+#
+#   2008-04-17
+#
+# COPYLEFT
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.
+
+AC_DEFUN([AC_CXX_COMPILE_STDCXX_0X], [
+  AC_CACHE_CHECK(if g++ supports C++0x features without additional flags,
+  ac_cv_cxx_compile_cxx0x_native,
+  [AC_LANG_SAVE
+  AC_LANG_CPLUSPLUS
+  AC_TRY_COMPILE([
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);],,
+  ac_cv_cxx_compile_cxx0x_native=yes, ac_cv_cxx_compile_cxx0x_native=no)
+  AC_LANG_RESTORE
+  ])
+
+  AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x,
+  ac_cv_cxx_compile_cxx0x_cxx,
+  [AC_LANG_SAVE
+  AC_LANG_CPLUSPLUS
+  ac_save_CXXFLAGS="$CXXFLAGS"
+  CXXFLAGS="$CXXFLAGS -std=c++0x"
+  AC_TRY_COMPILE([
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);],,
+  ac_cv_cxx_compile_cxx0x_cxx=yes, ac_cv_cxx_compile_cxx0x_cxx=no)
+  CXXFLAGS="$ac_save_CXXFLAGS"
+  AC_LANG_RESTORE
+  ])
+
+  AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x,
+  ac_cv_cxx_compile_cxx0x_gxx,
+  [AC_LANG_SAVE
+  AC_LANG_CPLUSPLUS
+  ac_save_CXXFLAGS="$CXXFLAGS"
+  CXXFLAGS="$CXXFLAGS -std=gnu++0x"
+  AC_TRY_COMPILE([
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);],,
+  ac_cv_cxx_compile_cxx0x_gxx=yes, ac_cv_cxx_compile_cxx0x_gxx=no)
+  CXXFLAGS="$ac_save_CXXFLAGS"
+  AC_LANG_RESTORE
+  ])
+
+  if test "$ac_cv_cxx_compile_cxx0x_native" = yes ||
+     test "$ac_cv_cxx_compile_cxx0x_cxx" = yes ||
+     test "$ac_cv_cxx_compile_cxx0x_gxx" = yes; then
+    AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ])
+  else
+    AC_MSG_ERROR([Could not find cxx0x support in g++])				
+  fi
+])
+ 
\ No newline at end of file
diff --git a/faux-folly/folly/m4/ax_boost_base.m4 b/faux-folly/folly/m4/ax_boost_base.m4
new file mode 100644
index 0000000..8e6ee9a
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_base.m4
@@ -0,0 +1,272 @@
+# ===========================================================================
+#       http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# DESCRIPTION
+#
+#   Test for the Boost C++ libraries of a particular version (or newer)
+#
+#   If no path to the installed boost library is given the macro searchs
+#   under /usr, /usr/local, /opt and /opt/local and evaluates the
+#   $BOOST_ROOT environment variable. Further documentation is available at
+#   <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
+#
+#   And sets:
+#
+#     HAVE_BOOST
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
+#   Copyright (c) 2009 Peter Adolphs
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 23
+
+AC_DEFUN([AX_BOOST_BASE],
+[
+AC_ARG_WITH([boost],
+  [AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
+    [use Boost library from a standard location (ARG=yes),
+     from the specified location (ARG=<path>),
+     or disable it (ARG=no)
+     @<:@ARG=yes@:>@ ])],
+    [
+    if test "$withval" = "no"; then
+        want_boost="no"
+    elif test "$withval" = "yes"; then
+        want_boost="yes"
+        ac_boost_path=""
+    else
+        want_boost="yes"
+        ac_boost_path="$withval"
+    fi
+    ],
+    [want_boost="yes"])
+
+
+AC_ARG_WITH([boost-libdir],
+        AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
+        [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
+        [
+        if test -d "$withval"
+        then
+                ac_boost_lib_path="$withval"
+        else
+                AC_MSG_ERROR(--with-boost-libdir expected directory name)
+        fi
+        ],
+        [ac_boost_lib_path=""]
+)
+
+if test "x$want_boost" = "xyes"; then
+    boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
+    boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
+    boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
+    boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
+    boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
+    if test "x$boost_lib_version_req_sub_minor" = "x" ; then
+        boost_lib_version_req_sub_minor="0"
+        fi
+    WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+  $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
+    AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
+    succeeded=no
+
+    dnl On 64-bit systems check for system libraries in both lib64 and lib.
+    dnl The former is specified by FHS, but e.g. Debian does not adhere to
+    dnl this (as it rises problems for generic multi-arch support).
+    dnl The last entry in the list is chosen by default when no libraries
+    dnl are found, e.g. when only header-only libraries are installed!
+    libsubdirs="lib"
+    ax_arch=`uname -m`
+    case $ax_arch in
+      x86_64|ppc64|s390x|sparc64|aarch64)
+        libsubdirs="lib64 lib lib64"
+        ;;
+    esac
+
+    dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
+    dnl them priority over the other paths since, if libs are found there, they
+    dnl are almost assuredly the ones desired.
+    AC_REQUIRE([AC_CANONICAL_HOST])
+    libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
+
+    case ${host_cpu} in
+      i?86)
+        libsubdirs="lib/i386-${host_os} $libsubdirs"
+        ;;
+    esac
+
+    dnl first we check the system location for boost libraries
+    dnl this location ist chosen if boost libraries are installed with the --layout=system option
+    dnl or if you install boost with RPM
+    if test "$ac_boost_path" != ""; then
+        BOOST_CPPFLAGS="-I$ac_boost_path/include"
+        for ac_boost_path_tmp in $libsubdirs; do
+                if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
+                        BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
+                        break
+                fi
+        done
+    elif test "$cross_compiling" != yes; then
+        for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
+            if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
+                for libsubdir in $libsubdirs ; do
+                    if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+                done
+                BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
+                BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
+                break;
+            fi
+        done
+    fi
+
+    dnl overwrite ld flags if we have required special directory with
+    dnl --with-boost-libdir parameter
+    if test "$ac_boost_lib_path" != ""; then
+       BOOST_LDFLAGS="-L$ac_boost_lib_path"
+    fi
+
+    CPPFLAGS_SAVED="$CPPFLAGS"
+    CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+    export CPPFLAGS
+
+    LDFLAGS_SAVED="$LDFLAGS"
+    LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+    export LDFLAGS
+
+    AC_REQUIRE([AC_PROG_CXX])
+    AC_LANG_PUSH(C++)
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+    @%:@include <boost/version.hpp>
+    ]], [[
+    #if BOOST_VERSION >= $WANT_BOOST_VERSION
+    // Everything is okay
+    #else
+    #  error Boost version is too old
+    #endif
+    ]])],[
+        AC_MSG_RESULT(yes)
+    succeeded=yes
+    found_system=yes
+        ],[
+        ])
+    AC_LANG_POP([C++])
+
+
+
+    dnl if we found no boost with system layout we search for boost libraries
+    dnl built and installed without the --layout=system option or for a staged(not installed) version
+    if test "x$succeeded" != "xyes"; then
+        _version=0
+        if test "$ac_boost_path" != ""; then
+            if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
+                for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
+                    _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
+                    V_CHECK=`expr $_version_tmp \> $_version`
+                    if test "$V_CHECK" = "1" ; then
+                        _version=$_version_tmp
+                    fi
+                    VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
+                    BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
+                done
+            fi
+        else
+            if test "$cross_compiling" != yes; then
+                for ac_boost_path in /usr /usr/local /opt /opt/local ; do
+                    if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
+                        for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
+                            _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
+                            V_CHECK=`expr $_version_tmp \> $_version`
+                            if test "$V_CHECK" = "1" ; then
+                                _version=$_version_tmp
+                                best_path=$ac_boost_path
+                            fi
+                        done
+                    fi
+                done
+
+                VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
+                BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
+                if test "$ac_boost_lib_path" = ""; then
+                    for libsubdir in $libsubdirs ; do
+                        if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+                    done
+                    BOOST_LDFLAGS="-L$best_path/$libsubdir"
+                fi
+            fi
+
+            if test "x$BOOST_ROOT" != "x"; then
+                for libsubdir in $libsubdirs ; do
+                    if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+                done
+                if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
+                    version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
+                    stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
+                        stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
+                    V_CHECK=`expr $stage_version_shorten \>\= $_version`
+                    if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
+                        AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
+                        BOOST_CPPFLAGS="-I$BOOST_ROOT"
+                        BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
+                    fi
+                fi
+            fi
+        fi
+
+        CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+        export CPPFLAGS
+        LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+        export LDFLAGS
+
+        AC_LANG_PUSH(C++)
+            AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+        @%:@include <boost/version.hpp>
+        ]], [[
+        #if BOOST_VERSION >= $WANT_BOOST_VERSION
+        // Everything is okay
+        #else
+        #  error Boost version is too old
+        #endif
+        ]])],[
+            AC_MSG_RESULT(yes)
+        succeeded=yes
+        found_system=yes
+            ],[
+            ])
+        AC_LANG_POP([C++])
+    fi
+
+    if test "$succeeded" != "yes" ; then
+        if test "$_version" = "0" ; then
+            AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option.  If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
+        else
+            AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
+        fi
+        # execute ACTION-IF-NOT-FOUND (if present):
+        ifelse([$3], , :, [$3])
+    else
+        AC_SUBST(BOOST_CPPFLAGS)
+        AC_SUBST(BOOST_LDFLAGS)
+        AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
+        # execute ACTION-IF-FOUND (if present):
+        ifelse([$2], , :, [$2])
+    fi
+
+    CPPFLAGS="$CPPFLAGS_SAVED"
+    LDFLAGS="$LDFLAGS_SAVED"
+fi
+
+])
diff --git a/faux-folly/folly/m4/ax_boost_context.m4 b/faux-folly/folly/m4/ax_boost_context.m4
new file mode 100644
index 0000000..6843c39
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_context.m4
@@ -0,0 +1,131 @@
+# ===========================================================================
+#     http://www.gnu.org/software/autoconf-archive/ax_boost_context.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_CONTEXT
+#
+# DESCRIPTION
+#
+#   Test for Context library from the Boost C++ libraries. The macro
+#   requires a preceding call to AX_BOOST_BASE. Further documentation is
+#   available at <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_CONTEXT_LIB)
+#
+#   And sets:
+#
+#     HAVE_BOOST_CONTEXT
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
+#   Copyright (c) 2008 Michael Tindal
+#   Copyright (c) 2013 Daniel Casimiro <dan.casimiro@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 1
+
+AC_DEFUN([AX_BOOST_CONTEXT],
+[
+	AC_ARG_WITH([boost-context],
+		AS_HELP_STRING([--with-boost-context@<:@=special-lib@:>@],
+		[use the Context library from boost - it is possible to specify a certain library for the linker
+			e.g. --with-boost-context=boost_context-gcc-mt ]), [
+		if test "$withval" = "no"; then
+			want_boost="no"
+		elif test "$withval" = "yes"; then
+			want_boost="yes"
+			ax_boost_user_context_lib=""
+		else
+			want_boost="yes"
+			ax_boost_user_context_lib="$withval"
+		fi
+		], [want_boost="yes"]
+	)
+
+	if test "x$want_boost" = "xyes"; then
+		AC_REQUIRE([AC_PROG_CC])
+		AC_REQUIRE([AC_CANONICAL_BUILD])
+
+		CPPFLAGS_SAVED="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+		export CPPFLAGS
+
+		LDFLAGS_SAVED="$LDFLAGS"
+		LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+		export LDFLAGS
+
+		AC_CACHE_CHECK(whether the Boost::Context library is available,
+			ax_cv_boost_context,
+			[AC_LANG_PUSH([C++])
+			CXXFLAGS_SAVE=$CXXFLAGS
+
+			AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+				[[@%:@include <boost/context/all.hpp>
+#include <boost/version.hpp>
+]],
+				[[#if BOOST_VERSION >= 105600
+  boost::context::fcontext_t fc = boost::context::make_fcontext(0, 0, 0);
+#else
+  boost::context::fcontext_t* fc = boost::context::make_fcontext(0, 0, 0);
+#endif
+]]
+				)],
+				ax_cv_boost_context=yes, ax_cv_boost_context=no)
+				CXXFLAGS=$CXXFLAGS_SAVE
+			AC_LANG_POP([C++])
+		])
+
+		if test "x$ax_cv_boost_context" = "xyes"; then
+			AC_SUBST(BOOST_CPPFLAGS)
+
+			AC_DEFINE(HAVE_BOOST_CONTEXT,,[define if the Boost::Context library is available])
+			BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+
+			if test "x$ax_boost_user_context_lib" = "x"; then
+				for libextension in `ls $BOOSTLIBDIR/libboost_context*.so* $BOOSTLIBDIR/libboost_context*.dylib* $BOOSTLIBDIR/libboost_context*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_context.*\)\.so.*$;\1;' -e 's;^lib\(boost_context.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_context.*\)\.a.*$;\1;'` ; do
+					ax_lib=${libextension}
+					AC_CHECK_LIB($ax_lib, exit,
+						[BOOST_CONTEXT_LIB="-l$ax_lib"; AC_SUBST(BOOST_CONTEXT_LIB) link_context="yes"; break],
+					[link_context="no"])
+				done
+
+				if test "x$link_context" != "xyes"; then
+					for libextension in `ls $BOOSTLIBDIR/boost_context*.dll* $BOOSTLIBDIR/boost_context*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_context.*\)\.dll.*$;\1;' -e 's;^\(boost_context.*\)\.a.*$;\1;'` ; do
+						ax_lib=${libextension}
+						AC_CHECK_LIB($ax_lib, exit,
+							[BOOST_CONTEXT_LIB="-l$ax_lib"; AC_SUBST(BOOST_CONTEXT_LIB) link_context="yes"; break],
+							[link_context="no"])
+					done
+				fi
+
+			else
+				for ax_lib in $ax_boost_user_context_lib boost_context-$ax_boost_user_context_lib; do
+					AC_CHECK_LIB($ax_lib, exit,
+						[BOOST_CONTEXT_LIB="-l$ax_lib"; AC_SUBST(BOOST_CONTEXT_LIB) link_context="yes"; break],
+						[link_context="no"])
+				done
+			fi
+
+			if test "x$ax_lib" = "x"; then
+				AC_MSG_ERROR(Could not find a version of the library!)
+			fi
+
+			if test "x$link_context" = "xno"; then
+				AC_MSG_ERROR(Could not link against $ax_lib !)
+			fi
+		fi
+
+		CPPFLAGS="$CPPFLAGS_SAVED"
+		LDFLAGS="$LDFLAGS_SAVED"
+	fi
+])
+
diff --git a/faux-folly/folly/m4/ax_boost_filesystem.m4 b/faux-folly/folly/m4/ax_boost_filesystem.m4
new file mode 100644
index 0000000..1d3c008
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_filesystem.m4
@@ -0,0 +1,118 @@
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_boost_filesystem.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_FILESYSTEM
+#
+# DESCRIPTION
+#
+#   Test for Filesystem library from the Boost C++ libraries. The macro
+#   requires a preceding call to AX_BOOST_BASE. Further documentation is
+#   available at <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_FILESYSTEM_LIB)
+#
+#   And sets:
+#
+#     HAVE_BOOST_FILESYSTEM
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
+#   Copyright (c) 2009 Michael Tindal
+#   Copyright (c) 2009 Roman Rybalko <libtorrent@romanr.info>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 26
+
+AC_DEFUN([AX_BOOST_FILESYSTEM],
+[
+	AC_ARG_WITH([boost-filesystem],
+	AS_HELP_STRING([--with-boost-filesystem@<:@=special-lib@:>@],
+                   [use the Filesystem library from boost - it is possible to specify a certain library for the linker
+                        e.g. --with-boost-filesystem=boost_filesystem-gcc-mt ]),
+        [
+        if test "$withval" = "no"; then
+	   	want_boost="no"
+        elif test "$withval" = "yes"; then
+            want_boost="yes"
+            ax_boost_user_filesystem_lib=""
+        else
+		    want_boost="yes"
+			ax_boost_user_filesystem_lib="$withval"
+				fi
+        ],
+        [want_boost="yes"]
+	)
+
+	if test "x$want_boost" = "xyes"; then
+        AC_REQUIRE([AC_PROG_CC])
+		CPPFLAGS_SAVED="$CPPFLAGS"
+			CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+					    export CPPFLAGS
+
+					    	   LDFLAGS_SAVED="$LDFLAGS"
+							LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+									  export LDFLAGS
+
+									  	 LIBS_SAVED=$LIBS
+											LIBS="$LIBS $BOOST_SYSTEM_LIB"
+												    export LIBS
+
+        AC_CACHE_CHECK(whether the Boost::Filesystem library is available,
+			       	   		     	        ax_cv_boost_filesystem,
+        [AC_LANG_PUSH([C++])
+         AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/filesystem/path.hpp>]],
+                                   [[using namespace boost::filesystem;
+                                   path my_path( "foo/bar/data.txt" );
+                                   return 0;]])],
+								       ax_cv_boost_filesystem=yes, ax_cv_boost_filesystem=no)
+         AC_LANG_POP([C++])
+		])
+			if test "x$ax_cv_boost_filesystem" = "xyes"; then
+			   	AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available])
+            BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+            if test "x$ax_boost_user_filesystem_lib" = "x"; then
+                for libextension in `ls -r $BOOSTLIBDIR/libboost_filesystem* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
+                     ax_lib=${libextension}
+					    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break],
+                                 [link_filesystem="no"])
+							done
+                if test "x$link_filesystem" != "xyes"; then
+                for libextension in `ls -r $BOOSTLIBDIR/boost_filesystem* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
+                     ax_lib=${libextension}
+					    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break],
+                                 [link_filesystem="no"])
+							done
+								    fi
+            else
+               for ax_lib in $ax_boost_user_filesystem_lib boost_filesystem-$ax_boost_user_filesystem_lib; do
+	       	   	           AC_CHECK_LIB($ax_lib, exit,
+                                   [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break],
+                                   [link_filesystem="no"])
+                  done
+
+            fi
+            if test "x$ax_lib" = "x"; then
+                AC_MSG_ERROR(Could not find a version of the library!)
+            fi
+			if test "x$link_filesystem" != "xyes"; then
+			   			    AC_MSG_ERROR(Could not link against $ax_lib !)
+						    		       	   fi
+										fi
+
+											CPPFLAGS="$CPPFLAGS_SAVED"
+												LDFLAGS="$LDFLAGS_SAVED"
+													LIBS="$LIBS_SAVED"
+													fi
+])
\ No newline at end of file
diff --git a/faux-folly/folly/m4/ax_boost_program_options.m4 b/faux-folly/folly/m4/ax_boost_program_options.m4
new file mode 100644
index 0000000..30913f6
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_program_options.m4
@@ -0,0 +1,108 @@
+# ============================================================================
+#  http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_PROGRAM_OPTIONS
+#
+# DESCRIPTION
+#
+#   Test for program options library from the Boost C++ libraries. The macro
+#   requires a preceding call to AX_BOOST_BASE. Further documentation is
+#   available at <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB)
+#
+#   And sets:
+#
+#     HAVE_BOOST_PROGRAM_OPTIONS
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 24
+
+AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS],
+[
+	AC_ARG_WITH([boost-program-options],
+		AS_HELP_STRING([--with-boost-program-options@<:@=special-lib@:>@],
+                       [use the program options library from boost - it is possible to specify a certain library for the linker
+                        e.g. --with-boost-program-options=boost_program_options-gcc-mt-1_33_1 ]),
+        [
+        if test "$withval" = "no"; then
+			want_boost="no"
+        elif test "$withval" = "yes"; then
+            want_boost="yes"
+            ax_boost_user_program_options_lib=""
+        else
+		    want_boost="yes"
+		ax_boost_user_program_options_lib="$withval"
+		fi
+        ],
+        [want_boost="yes"]
+	)
+
+	if test "x$want_boost" = "xyes"; then
+        AC_REQUIRE([AC_PROG_CC])
+	    export want_boost
+		CPPFLAGS_SAVED="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+		export CPPFLAGS
+		LDFLAGS_SAVED="$LDFLAGS"
+		LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+		export LDFLAGS
+		AC_CACHE_CHECK([whether the Boost::Program_Options library is available],
+					   ax_cv_boost_program_options,
+					   [AC_LANG_PUSH(C++)
+				AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/program_options/errors.hpp>
+                                                          ]],
+                                  [[boost::program_options::error err("Error message");
+                                   return 0;]])],
+                           ax_cv_boost_program_options=yes, ax_cv_boost_program_options=no)
+					AC_LANG_POP([C++])
+		])
+		if test "$ax_cv_boost_program_options" = yes; then
+				AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available])
+                  BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+                if test "x$ax_boost_user_program_options_lib" = "x"; then
+                for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break],
+                                 [link_program_options="no"])
+				done
+                if test "x$link_program_options" != "xyes"; then
+                for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break],
+                                 [link_program_options="no"])
+				done
+                fi
+                else
+                  for ax_lib in $ax_boost_user_program_options_lib boost_program_options-$ax_boost_user_program_options_lib; do
+				      AC_CHECK_LIB($ax_lib, main,
+                                   [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break],
+                                   [link_program_options="no"])
+                  done
+                fi
+            if test "x$ax_lib" = "x"; then
+                AC_MSG_ERROR(Could not find a version of the library!)
+            fi
+				if test "x$link_program_options" != "xyes"; then
+					AC_MSG_ERROR([Could not link against [$ax_lib] !])
+				fi
+		fi
+		CPPFLAGS="$CPPFLAGS_SAVED"
+	LDFLAGS="$LDFLAGS_SAVED"
+	fi
+])
diff --git a/faux-folly/folly/m4/ax_boost_regex.m4 b/faux-folly/folly/m4/ax_boost_regex.m4
new file mode 100644
index 0000000..918f16a
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_regex.m4
@@ -0,0 +1,111 @@
+# ===========================================================================
+#      http://www.gnu.org/software/autoconf-archive/ax_boost_regex.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_REGEX
+#
+# DESCRIPTION
+#
+#   Test for Regex library from the Boost C++ libraries. The macro requires
+#   a preceding call to AX_BOOST_BASE. Further documentation is available at
+#   <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_REGEX_LIB)
+#
+#   And sets:
+#
+#     HAVE_BOOST_REGEX
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
+#   Copyright (c) 2008 Michael Tindal
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 22
+
+AC_DEFUN([AX_BOOST_REGEX],
+[
+	AC_ARG_WITH([boost-regex],
+	AS_HELP_STRING([--with-boost-regex@<:@=special-lib@:>@],
+                   [use the Regex library from boost - it is possible to specify a certain library for the linker
+                        e.g. --with-boost-regex=boost_regex-gcc-mt-d-1_33_1 ]),
+        [
+        if test "$withval" = "no"; then
+			want_boost="no"
+        elif test "$withval" = "yes"; then
+            want_boost="yes"
+            ax_boost_user_regex_lib=""
+        else
+		    want_boost="yes"
+		ax_boost_user_regex_lib="$withval"
+		fi
+        ],
+        [want_boost="yes"]
+	)
+
+	if test "x$want_boost" = "xyes"; then
+        AC_REQUIRE([AC_PROG_CC])
+		CPPFLAGS_SAVED="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+		export CPPFLAGS
+
+		LDFLAGS_SAVED="$LDFLAGS"
+		LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+		export LDFLAGS
+
+        AC_CACHE_CHECK(whether the Boost::Regex library is available,
+					   ax_cv_boost_regex,
+        [AC_LANG_PUSH([C++])
+			 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/regex.hpp>
+												]],
+                                   [[boost::regex r(); return 0;]])],
+                   ax_cv_boost_regex=yes, ax_cv_boost_regex=no)
+         AC_LANG_POP([C++])
+		])
+		if test "x$ax_cv_boost_regex" = "xyes"; then
+			AC_DEFINE(HAVE_BOOST_REGEX,,[define if the Boost::Regex library is available])
+            BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+            if test "x$ax_boost_user_regex_lib" = "x"; then
+                for libextension in `ls $BOOSTLIBDIR/libboost_regex*.so* $BOOSTLIBDIR/libboost_regex*.dylib* $BOOSTLIBDIR/libboost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_regex.*\)\.so.*$;\1;' -e 's;^lib\(boost_regex.*\)\.dylib.*;\1;' -e 's;^lib\(boost_regex.*\)\.a.*$;\1;'` ; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break],
+                                 [link_regex="no"])
+				done
+                if test "x$link_regex" != "xyes"; then
+                for libextension in `ls $BOOSTLIBDIR/boost_regex*.dll* $BOOSTLIBDIR/boost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_regex.*\)\.dll.*$;\1;' -e 's;^\(boost_regex.*\)\.a.*$;\1;'` ; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break],
+                                 [link_regex="no"])
+				done
+                fi
+
+            else
+               for ax_lib in $ax_boost_user_regex_lib boost_regex-$ax_boost_user_regex_lib; do
+				      AC_CHECK_LIB($ax_lib, main,
+                                   [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break],
+                                   [link_regex="no"])
+               done
+            fi
+            if test "x$ax_lib" = "x"; then
+                AC_MSG_ERROR(Could not find a version of the Boost::Regex library!)
+            fi
+			if test "x$link_regex" != "xyes"; then
+				AC_MSG_ERROR(Could not link against $ax_lib !)
+			fi
+		fi
+
+		CPPFLAGS="$CPPFLAGS_SAVED"
+	LDFLAGS="$LDFLAGS_SAVED"
+	fi
+])
diff --git a/faux-folly/folly/m4/ax_boost_system.m4 b/faux-folly/folly/m4/ax_boost_system.m4
new file mode 100644
index 0000000..c4c4555
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_system.m4
@@ -0,0 +1,120 @@
+# ===========================================================================
+#      http://www.gnu.org/software/autoconf-archive/ax_boost_system.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_SYSTEM
+#
+# DESCRIPTION
+#
+#   Test for System library from the Boost C++ libraries. The macro requires
+#   a preceding call to AX_BOOST_BASE. Further documentation is available at
+#   <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_SYSTEM_LIB)
+#
+#   And sets:
+#
+#     HAVE_BOOST_SYSTEM
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
+#   Copyright (c) 2008 Michael Tindal
+#   Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 17
+
+AC_DEFUN([AX_BOOST_SYSTEM],
+[
+	AC_ARG_WITH([boost-system],
+	AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@],
+                   [use the System library from boost - it is possible to specify a certain library for the linker
+                        e.g. --with-boost-system=boost_system-gcc-mt ]),
+        [
+        if test "$withval" = "no"; then
+			want_boost="no"
+        elif test "$withval" = "yes"; then
+            want_boost="yes"
+            ax_boost_user_system_lib=""
+        else
+		    want_boost="yes"
+		ax_boost_user_system_lib="$withval"
+		fi
+        ],
+        [want_boost="yes"]
+	)
+
+	if test "x$want_boost" = "xyes"; then
+        AC_REQUIRE([AC_PROG_CC])
+        AC_REQUIRE([AC_CANONICAL_BUILD])
+		CPPFLAGS_SAVED="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+		export CPPFLAGS
+
+		LDFLAGS_SAVED="$LDFLAGS"
+		LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+		export LDFLAGS
+
+        AC_CACHE_CHECK(whether the Boost::System library is available,
+					   ax_cv_boost_system,
+        [AC_LANG_PUSH([C++])
+			 CXXFLAGS_SAVE=$CXXFLAGS
+
+			 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]],
+                                   [[boost::system::system_category]])],
+                   ax_cv_boost_system=yes, ax_cv_boost_system=no)
+			 CXXFLAGS=$CXXFLAGS_SAVE
+             AC_LANG_POP([C++])
+		])
+		if test "x$ax_cv_boost_system" = "xyes"; then
+			AC_SUBST(BOOST_CPPFLAGS)
+
+			AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available])
+            BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+
+			LDFLAGS_SAVE=$LDFLAGS
+            if test "x$ax_boost_user_system_lib" = "x"; then
+                for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
+                                 [link_system="no"])
+				done
+                if test "x$link_system" != "xyes"; then
+                for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
+                                 [link_system="no"])
+				done
+                fi
+
+            else
+               for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
+				      AC_CHECK_LIB($ax_lib, exit,
+                                   [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
+                                   [link_system="no"])
+                  done
+
+            fi
+            if test "x$ax_lib" = "x"; then
+                AC_MSG_ERROR(Could not find a version of the library!)
+            fi
+			if test "x$link_system" = "xno"; then
+				AC_MSG_ERROR(Could not link against $ax_lib !)
+			fi
+		fi
+
+		CPPFLAGS="$CPPFLAGS_SAVED"
+	LDFLAGS="$LDFLAGS_SAVED"
+	fi
+])
diff --git a/faux-folly/folly/m4/ax_boost_thread.m4 b/faux-folly/folly/m4/ax_boost_thread.m4
new file mode 100644
index 0000000..79e12cd
--- /dev/null
+++ b/faux-folly/folly/m4/ax_boost_thread.m4
@@ -0,0 +1,149 @@
+# ===========================================================================
+#      http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_THREAD
+#
+# DESCRIPTION
+#
+#   Test for Thread library from the Boost C++ libraries. The macro requires
+#   a preceding call to AX_BOOST_BASE. Further documentation is available at
+#   <http://randspringer.de/boost/index.html>.
+#
+#   This macro calls:
+#
+#     AC_SUBST(BOOST_THREAD_LIB)
+#
+#   And sets:
+#
+#     HAVE_BOOST_THREAD
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
+#   Copyright (c) 2009 Michael Tindal
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 27
+
+AC_DEFUN([AX_BOOST_THREAD],
+[
+	AC_ARG_WITH([boost-thread],
+	AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@],
+                   [use the Thread library from boost - it is possible to specify a certain library for the linker
+                        e.g. --with-boost-thread=boost_thread-gcc-mt ]),
+        [
+        if test "$withval" = "no"; then
+			want_boost="no"
+        elif test "$withval" = "yes"; then
+            want_boost="yes"
+            ax_boost_user_thread_lib=""
+        else
+		    want_boost="yes"
+		ax_boost_user_thread_lib="$withval"
+		fi
+        ],
+        [want_boost="yes"]
+	)
+
+	if test "x$want_boost" = "xyes"; then
+        AC_REQUIRE([AC_PROG_CC])
+        AC_REQUIRE([AC_CANONICAL_BUILD])
+		CPPFLAGS_SAVED="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+		export CPPFLAGS
+
+		LDFLAGS_SAVED="$LDFLAGS"
+		LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+		export LDFLAGS
+
+        AC_CACHE_CHECK(whether the Boost::Thread library is available,
+					   ax_cv_boost_thread,
+        [AC_LANG_PUSH([C++])
+			 CXXFLAGS_SAVE=$CXXFLAGS
+
+			 if test "x$host_os" = "xsolaris" ; then
+				 CXXFLAGS="-pthreads $CXXFLAGS"
+			 elif test "x$host_os" = "xmingw32" ; then
+				 CXXFLAGS="-mthreads $CXXFLAGS"
+			 else
+				CXXFLAGS="-pthread $CXXFLAGS"
+			 fi
+			 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/thread/thread.hpp>]],
+                                   [[boost::thread_group thrds;
+                                   return 0;]])],
+                   ax_cv_boost_thread=yes, ax_cv_boost_thread=no)
+			 CXXFLAGS=$CXXFLAGS_SAVE
+             AC_LANG_POP([C++])
+		])
+		if test "x$ax_cv_boost_thread" = "xyes"; then
+           if test "x$host_os" = "xsolaris" ; then
+			  BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
+		   elif test "x$host_os" = "xmingw32" ; then
+			  BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
+		   else
+			  BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
+		   fi
+
+			AC_SUBST(BOOST_CPPFLAGS)
+
+			AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available])
+            BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+
+			LDFLAGS_SAVE=$LDFLAGS
+                        case "x$host_os" in
+                          *bsd* )
+                               LDFLAGS="-pthread $LDFLAGS"
+                          break;
+                          ;;
+                        esac
+            if test "x$ax_boost_user_thread_lib" = "x"; then
+                for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
+                                 [link_thread="no"])
+				done
+                if test "x$link_thread" != "xyes"; then
+                for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do
+                     ax_lib=${libextension}
+				    AC_CHECK_LIB($ax_lib, exit,
+                                 [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
+                                 [link_thread="no"])
+				done
+                fi
+
+            else
+               for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do
+				      AC_CHECK_LIB($ax_lib, exit,
+                                   [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
+                                   [link_thread="no"])
+                  done
+
+            fi
+            if test "x$ax_lib" = "x"; then
+                AC_MSG_ERROR(Could not find a version of the library!)
+            fi
+			if test "x$link_thread" = "xno"; then
+				AC_MSG_ERROR(Could not link against $ax_lib !)
+                        else
+                           case "x$host_os" in
+                              *bsd* )
+				BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS"
+                              break;
+                              ;;
+                           esac
+
+			fi
+		fi
+
+		CPPFLAGS="$CPPFLAGS_SAVED"
+	LDFLAGS="$LDFLAGS_SAVED"
+	fi
+])
diff --git a/faux-folly/folly/m4/ax_config_feature.m4 b/faux-folly/folly/m4/ax_config_feature.m4
new file mode 100644
index 0000000..e205723
--- /dev/null
+++ b/faux-folly/folly/m4/ax_config_feature.m4
@@ -0,0 +1,156 @@
+# ===========================================================================
+#     http://www.gnu.org/software/autoconf-archive/ax_config_feature.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CONFIG_FEATURE(FEATURE-NAME, FEATURE-DESCRIPTION, DEFINE, DEFINE-DESCRIPTION, [ACTION-IF-ENABLED [, ACTION-IF-NOT-ENABLED]])
+#
+# DESCRIPTION
+#
+#   AX_CONFIG_FEATURE is a simple wrapper for AC_ARG_ENABLE, it enables the
+#   feature FEATURE-NAME and AC_DEFINEs the passed DEFINE, depending on the
+#   user choice. DESCRIPTION will be used for AC_DEFINEs. ACTION-IF-ENABLED
+#   and ACTION-IF-NOT-ENABLED are the actions that will be run. A feature is
+#   enabled by default, in order to change this behaviour use the
+#   AX_CONFIG_FEATURE_DEFAULT_ENABLED and AX_CONFIG_FEATURE_DEFAULT_DISABLED
+#   macros.
+#
+#   A simple example:
+#
+#     AX_CONFIG_FEATURE_DEFAULT_ENABLED
+#     AX_CONFIG_FEATURE(feature_xxxxx, [turns on/off XXXXX support],
+#                       HAVE_XXXXX, [Define if you want XXXXX support])
+#
+#     ...
+#
+#     AX_CONFIG_FEATURE_DEFAULT_DISABLED
+#     AX_CONFIG_FEATURE(feature_yyyyy, [turns on/off YYYYY support],
+#                       HAVE_YYYYY, [Define if you want YYYYY support],
+#                       [enable_yyyyy="yes"], [enable_yyyyy="no"])
+#     AM_CONDITIONAL(YYYYY, [test "$enable_yyyyy" = "yes"])
+#
+#     AX_CONFIG_FEATURE_DEFAULT_ENABLED
+#     AX_CONFIG_FEATURE(...)
+#
+#     ...
+#
+#   If you have lot of features and you want a verbose dumping of each user
+#   selection use AX_CONFIG_FEATURE_VERBOSE. Use AX_CONFIG_FEATURE_SILENT in
+#   order to remove a previously AX_CONFIG_FEATURE_VERBOSE. By default
+#   features are silent.
+#
+#   Use AX_CONFIG_FEATURE_ENABLE or AX_CONFIG_FEATURE_DISABLE in order to
+#   enable or disable a specific feature.
+#
+#   Another simple example:
+#
+#     AS_IF([some_test_here],[AX_CONFIG_FEATURE_ENABLE(feature_xxxxx)],[])
+#
+#     AX_CONFIG_FEATURE(feature_xxxxx, [turns on/off XXXXX support],
+#                       HAVE_XXXXX, [Define if you want XXXXX support])
+#     AX_CONFIG_FEATURE(feature_yyyyy, [turns on/off YYYYY support],
+#                       HAVE_YYYYY, [Define if you want YYYYY support],
+#                       [enable_yyyyy="yes"], [enable_yyyyy="no"])
+#
+#     ...
+#
+#   NOTE: AX_CONFIG_FEATURE_ENABLE() must be placed first of the relative
+#   AX_CONFIG_FEATURE() macro ...
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Francesco Salvestrini <salvestrini@users.sourceforge.net>
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 2 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 10
+
+AC_DEFUN([AX_CONFIG_FEATURE],[ dnl
+m4_pushdef([FEATURE], patsubst([$1], -, _))dnl
+
+AC_ARG_ENABLE([$1],AS_HELP_STRING([--enable-$1],[$2]),[
+case "${enableval}" in
+   yes)
+     ax_config_feature_[]FEATURE[]="yes"
+     ;;
+   no)
+     ax_config_feature_[]FEATURE[]="no"
+     ;;
+   *)
+     AC_MSG_ERROR([bad value ${enableval} for feature --$1])
+     ;;
+esac
+])
+
+AS_IF([test "$ax_config_feature_[]FEATURE[]" = yes],[ dnl
+  AC_DEFINE([$3])
+  $5
+  AS_IF([test "$ax_config_feature_verbose" = yes],[ dnl
+    AC_MSG_NOTICE([Feature $1 is enabled])
+  ])
+],[ dnl
+  $6
+  AS_IF([test "$ax_config_feature_verbose" = yes],[ dnl
+    AC_MSG_NOTICE([Feature $1 is disabled])
+  ])
+])
+
+AH_TEMPLATE([$3],[$4])
+
+m4_popdef([FEATURE])dnl
+])
+
+dnl Feature global
+AC_DEFUN([AX_CONFIG_FEATURE_VERBOSE],[ dnl
+  ax_config_feature_verbose=yes
+])
+
+dnl Feature global
+AC_DEFUN([AX_CONFIG_FEATURE_SILENT],[ dnl
+  ax_config_feature_verbose=no
+])
+
+dnl Feature specific
+AC_DEFUN([AX_CONFIG_FEATURE_DEFAULT_ENABLED], [
+  ax_config_feature_[]FEATURE[]_default=yes
+])
+
+dnl Feature specific
+AC_DEFUN([AX_CONFIG_FEATURE_DEFAULT_DISABLED], [
+  ax_config_feature_[]FEATURE[]_default=no
+])
+
+dnl Feature specific
+AC_DEFUN([AX_CONFIG_FEATURE_ENABLE],[ dnl
+  ax_config_feature_[]patsubst([$1], -, _)[]=yes
+])
+
+dnl Feature specific
+AC_DEFUN([AX_CONFIG_FEATURE_DISABLE],[ dnl
+  ax_config_feature_[]patsubst([$1], -, _)[]=no
+])
diff --git a/faux-folly/folly/m4/ax_prefix_config.m4 b/faux-folly/folly/m4/ax_prefix_config.m4
new file mode 100644
index 0000000..c40d2df
--- /dev/null
+++ b/faux-folly/folly/m4/ax_prefix_config.m4
@@ -0,0 +1,209 @@
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_prefix_config_h.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PREFIX_CONFIG_H [(OUTPUT-HEADER [,PREFIX [,ORIG-HEADER]])]
+#
+# DESCRIPTION
+#
+#   This is a new variant from ac_prefix_config_ this one will use a
+#   lowercase-prefix if the config-define was starting with a
+#   lowercase-char, e.g. "#define const", "#define restrict", or "#define
+#   off_t", (and this one can live in another directory, e.g.
+#   testpkg/config.h therefore I decided to move the output-header to be the
+#   first arg)
+#
+#   takes the usual config.h generated header file; looks for each of the
+#   generated "#define SOMEDEF" lines, and prefixes the defined name (ie.
+#   makes it "#define PREFIX_SOMEDEF". The result is written to the output
+#   config.header file. The PREFIX is converted to uppercase for the
+#   conversions.
+#
+#   Defaults:
+#
+#     OUTPUT-HEADER = $PACKAGE-config.h
+#     PREFIX = $PACKAGE
+#     ORIG-HEADER, from AM_CONFIG_HEADER(config.h)
+#
+#   Your configure.ac script should contain both macros in this order, and
+#   unlike the earlier variations of this prefix-macro it is okay to place
+#   the AX_PREFIX_CONFIG_H call before the AC_OUTPUT invokation.
+#
+#   Example:
+#
+#     AC_INIT(config.h.in)        # config.h.in as created by "autoheader"
+#     AM_INIT_AUTOMAKE(testpkg, 0.1.1)    # makes #undef VERSION and PACKAGE
+#     AM_CONFIG_HEADER(config.h)          # prep config.h from config.h.in
+#     AX_PREFIX_CONFIG_H(mylib/_config.h) # prep mylib/_config.h from it..
+#     AC_MEMORY_H                         # makes "#undef NEED_MEMORY_H"
+#     AC_C_CONST_H                        # makes "#undef const"
+#     AC_OUTPUT(Makefile)                 # creates the "config.h" now
+#                                         # and also mylib/_config.h
+#
+#   if the argument to AX_PREFIX_CONFIG_H would have been omitted then the
+#   default outputfile would have been called simply "testpkg-config.h", but
+#   even under the name "mylib/_config.h" it contains prefix-defines like
+#
+#     #ifndef TESTPKG_VERSION
+#     #define TESTPKG_VERSION "0.1.1"
+#     #endif
+#     #ifndef TESTPKG_NEED_MEMORY_H
+#     #define TESTPKG_NEED_MEMORY_H 1
+#     #endif
+#     #ifndef _testpkg_const
+#     #define _testpkg_const _const
+#     #endif
+#
+#   and this "mylib/_config.h" can be installed along with other
+#   header-files, which is most convenient when creating a shared library
+#   (that has some headers) where some functionality is dependent on the
+#   OS-features detected at compile-time. No need to invent some
+#   "mylib-confdefs.h.in" manually. :-)
+#
+#   Note that some AC_DEFINEs that end up in the config.h file are actually
+#   self-referential - e.g. AC_C_INLINE, AC_C_CONST, and the AC_TYPE_OFF_T
+#   say that they "will define inline|const|off_t if the system does not do
+#   it by itself". You might want to clean up about these - consider an
+#   extra mylib/conf.h that reads something like:
+#
+#     #include <mylib/_config.h>
+#     #ifndef _testpkg_const
+#     #define _testpkg_const const
+#     #endif
+#
+#   and then start using _testpkg_const in the header files. That is also a
+#   good thing to differentiate whether some library-user has starting to
+#   take up with a different compiler, so perhaps it could read something
+#   like this:
+#
+#     #ifdef _MSC_VER
+#     #include <mylib/_msvc.h>
+#     #else
+#     #include <mylib/_config.h>
+#     #endif
+#     #ifndef _testpkg_const
+#     #define _testpkg_const const
+#     #endif
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2008 Marten Svantesson
+#   Copyright (c) 2008 Gerald Point <Gerald.Point@labri.fr>
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 11
+
+AC_DEFUN([AX_PREFIX_CONFIG_H],[dnl
+AC_PREREQ([2.62])
+AC_BEFORE([AC_CONFIG_HEADERS],[$0])dnl
+AC_CONFIG_COMMANDS([ifelse($1,,$PACKAGE-config.h,$1)],[dnl
+AS_VAR_PUSHDEF([_OUT],[ac_prefix_conf_OUT])dnl
+AS_VAR_PUSHDEF([_DEF],[ac_prefix_conf_DEF])dnl
+AS_VAR_PUSHDEF([_PKG],[ac_prefix_conf_PKG])dnl
+AS_VAR_PUSHDEF([_LOW],[ac_prefix_conf_LOW])dnl
+AS_VAR_PUSHDEF([_UPP],[ac_prefix_conf_UPP])dnl
+AS_VAR_PUSHDEF([_INP],[ac_prefix_conf_INP])dnl
+m4_pushdef([_script],[conftest.prefix])dnl
+m4_pushdef([_symbol],[m4_cr_Letters[]m4_cr_digits[]_])dnl
+_OUT=`echo ifelse($1, , $PACKAGE-config.h, $1)`
+_DEF=`echo _$_OUT | sed -e "y:m4_cr_letters:m4_cr_LETTERS[]:" -e "s/@<:@^m4_cr_Letters@:>@/_/g"`
+_PKG=`echo ifelse($2, , $PACKAGE, $2)`
+_LOW=`echo _$_PKG | sed -e "y:m4_cr_LETTERS-:m4_cr_letters[]_:"`
+_UPP=`echo $_PKG | sed -e "y:m4_cr_letters-:m4_cr_LETTERS[]_:"  -e "/^@<:@m4_cr_digits@:>@/s/^/_/"`
+_INP=`echo "ifelse($3,,,$3)" | sed -e 's/ *//'`
+if test ".$_INP" = "."; then
+   for ac_file in : $CONFIG_HEADERS; do test "_$ac_file" = _: && continue
+     case "$ac_file" in
+        *.h) _INP=$ac_file ;;
+        *)
+     esac
+     test ".$_INP" != "." && break
+   done
+fi
+if test ".$_INP" = "."; then
+   case "$_OUT" in
+      */*) _INP=`basename "$_OUT"`
+      ;;
+      *-*) _INP=`echo "$_OUT" | sed -e "s/@<:@_symbol@:>@*-//"`
+      ;;
+      *) _INP=config.h
+      ;;
+   esac
+fi
+if test -z "$_PKG" ; then
+   AC_MSG_ERROR([no prefix for _PREFIX_PKG_CONFIG_H])
+else
+  if test ! -f "$_INP" ; then if test -f "$srcdir/$_INP" ; then
+     _INP="$srcdir/$_INP"
+  fi fi
+  AC_MSG_NOTICE(creating $_OUT - prefix $_UPP for $_INP defines)
+  if test -f $_INP ; then
+    AS_ECHO(["s/^@%:@undef  *\\(@<:@m4_cr_LETTERS[]_@:>@\\)/@%:@undef $_UPP""_\\1/"]) > _script
+    AS_ECHO(["s/^@%:@undef  *\\(@<:@m4_cr_letters@:>@\\)/@%:@undef $_LOW""_\\1/"]) >> _script
+    AS_ECHO(["s/^@%:@def[]ine  *\\(@<:@m4_cr_LETTERS[]_@:>@@<:@_symbol@:>@*\\)\\(.*\\)/@%:@ifndef $_UPP""_\\1\\"]) >> _script
+    AS_ECHO(["@%:@def[]ine $_UPP""_\\1\\2\\"]) >> _script
+    AS_ECHO(["@%:@endif/"]) >> _script
+    AS_ECHO(["s/^@%:@def[]ine  *\\(@<:@m4_cr_letters@:>@@<:@_symbol@:>@*\\)\\(.*\\)/@%:@ifndef $_LOW""_\\1\\"]) >> _script
+    AS_ECHO(["@%:@define $_LOW""_\\1\\2\\"]) >> _script
+    AS_ECHO(["@%:@endif/"]) >> _script
+    # now executing _script on _DEF input to create _OUT output file
+    echo "@%:@ifndef $_DEF"      >$tmp/pconfig.h
+    echo "@%:@def[]ine $_DEF 1" >>$tmp/pconfig.h
+    echo ' ' >>$tmp/pconfig.h
+    echo /'*' $_OUT. Generated automatically at end of configure. '*'/ >>$tmp/pconfig.h
+
+    sed -f _script $_INP >>$tmp/pconfig.h
+    echo ' ' >>$tmp/pconfig.h
+    echo '/* once:' $_DEF '*/' >>$tmp/pconfig.h
+    echo "@%:@endif" >>$tmp/pconfig.h
+    if cmp -s $_OUT $tmp/pconfig.h 2>/dev/null; then
+      AC_MSG_NOTICE([$_OUT is unchanged])
+    else
+      ac_dir=`AS_DIRNAME(["$_OUT"])`
+      AS_MKDIR_P(["$ac_dir"])
+      rm -f "$_OUT"
+      mv $tmp/pconfig.h "$_OUT"
+    fi
+    cp _script _configs.sed
+  else
+    AC_MSG_ERROR([input file $_INP does not exist - skip generating $_OUT])
+  fi
+  rm -f conftest.*
+fi
+m4_popdef([_symbol])dnl
+m4_popdef([_script])dnl
+AS_VAR_POPDEF([_INP])dnl
+AS_VAR_POPDEF([_UPP])dnl
+AS_VAR_POPDEF([_LOW])dnl
+AS_VAR_POPDEF([_PKG])dnl
+AS_VAR_POPDEF([_DEF])dnl
+AS_VAR_POPDEF([_OUT])dnl
+],[PACKAGE="$PACKAGE"])])
\ No newline at end of file
diff --git a/faux-folly/folly/stats/BucketedTimeSeries-defs.h b/faux-folly/folly/stats/BucketedTimeSeries-defs.h
new file mode 100644
index 0000000..2a51dec
--- /dev/null
+++ b/faux-folly/folly/stats/BucketedTimeSeries-defs.h
@@ -0,0 +1,464 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_STATS_BUCKETEDTIMESERIES_INL_H_
+#define FOLLY_STATS_BUCKETEDTIMESERIES_INL_H_
+
+#include <glog/logging.h>
+#include <folly/Likely.h>
+
+namespace folly {
+
+template <typename VT, typename TT>
+BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets,
+                                               TimeType maxDuration)
+  : firstTime_(1),
+    latestTime_(0),
+    duration_(maxDuration) {
+  // For tracking all-time data we only use total_, and don't need to bother
+  // with buckets_
+  if (!isAllTime()) {
+    // Round nBuckets down to duration_.count().
+    //
+    // There is no point in having more buckets than our timestamp
+    // granularity: otherwise we would have buckets that could never be used.
+    if (nBuckets > size_t(duration_.count())) {
+      nBuckets = duration_.count();
+    }
+
+    buckets_.resize(nBuckets, Bucket());
+  }
+}
+
+template <typename VT, typename TT>
+bool BucketedTimeSeries<VT, TT>::addValue(TimeType now, const ValueType& val) {
+  return addValueAggregated(now, val, 1);
+}
+
+template <typename VT, typename TT>
+bool BucketedTimeSeries<VT, TT>::addValue(TimeType now,
+                                          const ValueType& val,
+                                          int64_t times) {
+  return addValueAggregated(now, val * times, times);
+}
+
+template <typename VT, typename TT>
+bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now,
+                                                    const ValueType& total,
+                                                    int64_t nsamples) {
+  if (isAllTime()) {
+    if (UNLIKELY(empty())) {
+      firstTime_ = now;
+      latestTime_ = now;
+    } else if (now > latestTime_) {
+      latestTime_ = now;
+    } else if (now < firstTime_) {
+      firstTime_ = now;
+    }
+    total_.add(total, nsamples);
+    return true;
+  }
+
+  size_t bucketIdx;
+  if (UNLIKELY(empty())) {
+    // First data point we've ever seen
+    firstTime_ = now;
+    latestTime_ = now;
+    bucketIdx = getBucketIdx(now);
+  } else if (now > latestTime_) {
+    // More recent time.  Need to update the buckets.
+    bucketIdx = updateBuckets(now);
+  } else if (LIKELY(now == latestTime_)) {
+    // Current time.
+    bucketIdx = getBucketIdx(now);
+  } else {
+    // An earlier time in the past.  We need to check if this time still falls
+    // within our window.
+    if (now < getEarliestTimeNonEmpty()) {
+      return false;
+    }
+    bucketIdx = getBucketIdx(now);
+  }
+
+  total_.add(total, nsamples);
+  buckets_[bucketIdx].add(total, nsamples);
+  return true;
+}
+
+template <typename VT, typename TT>
+size_t BucketedTimeSeries<VT, TT>::update(TimeType now) {
+  if (empty()) {
+    // This is the first data point.
+    firstTime_ = now;
+  }
+
+  // For all-time data, all we need to do is update latestTime_
+  if (isAllTime()) {
+    latestTime_ = std::max(latestTime_, now);
+    return 0;
+  }
+
+  // Make sure time doesn't go backwards.
+  // If the time is less than or equal to the latest time we have already seen,
+  // we don't need to do anything.
+  if (now <= latestTime_) {
+    return getBucketIdx(latestTime_);
+  }
+
+  return updateBuckets(now);
+}
+
+template <typename VT, typename TT>
+size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) {
+  // We could cache nextBucketStart as a member variable, so we don't have to
+  // recompute it each time update() is called with a new timestamp value.
+  // This makes things faster when update() (or addValue()) is called once
+  // per second, but slightly slower when update() is called multiple times a
+  // second.  We care more about optimizing the cases where addValue() is being
+  // called frequently.  If addValue() is only being called once every few
+  // seconds, it doesn't matter as much if it is fast.
+
+  // Get info about the bucket that latestTime_ points at
+  size_t currentBucket;
+  TimeType currentBucketStart;
+  TimeType nextBucketStart;
+  getBucketInfo(latestTime_, &currentBucket,
+                &currentBucketStart, &nextBucketStart);
+
+  // Update latestTime_
+  latestTime_ = now;
+
+  if (now < nextBucketStart) {
+    // We're still in the same bucket.
+    // We're done after updating latestTime_.
+    return currentBucket;
+  } else if (now >= currentBucketStart + duration_) {
+    // It's been a while.  We have wrapped, and all of the buckets need to be
+    // cleared.
+    for (Bucket& bucket : buckets_) {
+      bucket.clear();
+    }
+    total_.clear();
+    return getBucketIdx(latestTime_);
+  } else {
+    // clear all the buckets between the last time and current time, meaning
+    // buckets in the range [(currentBucket+1), newBucket]. Note that
+    // the bucket (currentBucket+1) is always the oldest bucket we have. Since
+    // our array is circular, loop when we reach the end.
+    size_t newBucket = getBucketIdx(now);
+    size_t idx = currentBucket;
+    while (idx != newBucket) {
+      ++idx;
+      if (idx >= buckets_.size()) {
+        idx = 0;
+      }
+      total_ -= buckets_[idx];
+      buckets_[idx].clear();
+    }
+    return newBucket;
+  }
+}
+
+template <typename VT, typename TT>
+void BucketedTimeSeries<VT, TT>::clear() {
+  for (Bucket& bucket : buckets_) {
+    bucket.clear();
+  }
+  total_.clear();
+  // Set firstTime_ larger than latestTime_,
+  // to indicate that the timeseries is empty
+  firstTime_ = TimeType(1);
+  latestTime_ = TimeType(0);
+}
+
+
+template <typename VT, typename TT>
+TT BucketedTimeSeries<VT, TT>::getEarliestTime() const {
+  if (empty()) {
+    return TimeType(0);
+  }
+  if (isAllTime()) {
+    return firstTime_;
+  }
+
+  // Compute the earliest time we can track
+  TimeType earliestTime = getEarliestTimeNonEmpty();
+
+  // We're never tracking data before firstTime_
+  earliestTime = std::max(earliestTime, firstTime_);
+
+  return earliestTime;
+}
+
+template <typename VT, typename TT>
+TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const {
+  size_t currentBucket;
+  TimeType currentBucketStart;
+  TimeType nextBucketStart;
+  getBucketInfo(latestTime_, &currentBucket,
+                &currentBucketStart, &nextBucketStart);
+
+  // Subtract 1 duration from the start of the next bucket to find the
+  // earliest possible data point we could be tracking.
+  return nextBucketStart - duration_;
+}
+
+template <typename VT, typename TT>
+TT BucketedTimeSeries<VT, TT>::elapsed() const {
+  if (empty()) {
+    return TimeType(0);
+  }
+
+  // Add 1 since [latestTime_, earliestTime] is an inclusive interval.
+  return latestTime_ - getEarliestTime() + TimeType(1);
+}
+
+template <typename VT, typename TT>
+TT BucketedTimeSeries<VT, TT>::elapsed(TimeType start, TimeType end) const {
+  if (empty()) {
+    return TimeType(0);
+  }
+  start = std::max(start, getEarliestTime());
+  end = std::min(end, latestTime_ + TimeType(1));
+  end = std::max(start, end);
+  return end - start;
+}
+
+template <typename VT, typename TT>
+VT BucketedTimeSeries<VT, TT>::sum(TimeType start, TimeType end) const {
+  ValueType total = ValueType();
+  forEachBucket(start, end, [&](const Bucket& bucket,
+                                TimeType bucketStart,
+                                TimeType nextBucketStart) -> bool {
+    total += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
+                             bucket.sum);
+    return true;
+  });
+
+  return total;
+}
+
+template <typename VT, typename TT>
+uint64_t BucketedTimeSeries<VT, TT>::count(TimeType start, TimeType end) const {
+  uint64_t sample_count = 0;
+  forEachBucket(start, end, [&](const Bucket& bucket,
+                                TimeType bucketStart,
+                                TimeType nextBucketStart) -> bool {
+    sample_count += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
+                               bucket.count);
+    return true;
+  });
+
+  return sample_count;
+}
+
+template <typename VT, typename TT>
+template <typename ReturnType>
+ReturnType BucketedTimeSeries<VT, TT>::avg(TimeType start, TimeType end) const {
+  ValueType total = ValueType();
+  uint64_t sample_count = 0;
+  forEachBucket(start, end, [&](const Bucket& bucket,
+                                TimeType bucketStart,
+                                TimeType nextBucketStart) -> bool {
+    total += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
+                             bucket.sum);
+    sample_count += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
+                               bucket.count);
+    return true;
+  });
+
+  if (sample_count == 0) {
+    return ReturnType(0);
+  }
+
+  return detail::avgHelper<ReturnType>(total, sample_count);
+}
+
+/*
+ * A note about some of the bucket index calculations below:
+ *
+ * buckets_.size() may not divide evenly into duration_.  When this happens,
+ * some buckets will be wider than others.  We still want to spread the data
+ * out as evenly as possible among the buckets (as opposed to just making the
+ * last bucket be significantly wider than all of the others).
+ *
+ * To make the division work out, we pretend that the buckets are each
+ * duration_ wide, so that the overall duration becomes
+ * buckets.size() * duration_.
+ *
+ * To transform a real timestamp into the scale used by our buckets,
+ * we have to multiply by buckets_.size().  To figure out which bucket it goes
+ * into, we then divide by duration_.
+ */
+
+template <typename VT, typename TT>
+size_t BucketedTimeSeries<VT, TT>::getBucketIdx(TimeType time) const {
+  // For all-time data we don't use buckets_.  Everything is tracked in total_.
+  DCHECK(!isAllTime());
+
+  time %= duration_;
+  return time.count() * buckets_.size() / duration_.count();
+}
+
+/*
+ * Compute the bucket index for the specified time, as well as the earliest
+ * time that falls into this bucket.
+ */
+template <typename VT, typename TT>
+void BucketedTimeSeries<VT, TT>::getBucketInfo(
+    TimeType time, size_t *bucketIdx,
+    TimeType* bucketStart, TimeType* nextBucketStart) const {
+  typedef typename TimeType::rep TimeInt;
+  DCHECK(!isAllTime());
+
+  // Keep these two lines together.  The compiler should be able to compute
+  // both the division and modulus with a single operation.
+  TimeType timeMod = time % duration_;
+  TimeInt numFullDurations = time / duration_;
+
+  TimeInt scaledTime = timeMod.count() * buckets_.size();
+
+  // Keep these two lines together.  The compiler should be able to compute
+  // both the division and modulus with a single operation.
+  *bucketIdx = scaledTime / duration_.count();
+  TimeInt scaledOffsetInBucket = scaledTime % duration_.count();
+
+  TimeInt scaledBucketStart = scaledTime - scaledOffsetInBucket;
+  TimeInt scaledNextBucketStart = scaledBucketStart + duration_.count();
+
+  TimeType bucketStartMod((scaledBucketStart + buckets_.size() - 1) /
+                          buckets_.size());
+  TimeType nextBucketStartMod((scaledNextBucketStart + buckets_.size() - 1) /
+                              buckets_.size());
+
+  TimeType durationStart(numFullDurations * duration_.count());
+  *bucketStart = bucketStartMod + durationStart;
+  *nextBucketStart = nextBucketStartMod + durationStart;
+}
+
+template <typename VT, typename TT>
+template <typename Function>
+void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
+  if (isAllTime()) {
+    fn(total_, firstTime_, latestTime_ + TimeType(1));
+    return;
+  }
+
+  typedef typename TimeType::rep TimeInt;
+
+  // Compute durationStart, latestBucketIdx, and scaledNextBucketStart,
+  // the same way as in getBucketInfo().
+  TimeType timeMod = latestTime_ % duration_;
+  TimeInt numFullDurations = latestTime_ / duration_;
+  TimeType durationStart(numFullDurations * duration_.count());
+  TimeInt scaledTime = timeMod.count() * buckets_.size();
+  size_t latestBucketIdx = scaledTime / duration_.count();
+  TimeInt scaledOffsetInBucket = scaledTime % duration_.count();
+  TimeInt scaledBucketStart = scaledTime - scaledOffsetInBucket;
+  TimeInt scaledNextBucketStart = scaledBucketStart + duration_.count();
+
+  // Walk through the buckets, starting one past the current bucket.
+  // The next bucket is from the previous cycle, so subtract 1 duration
+  // from durationStart.
+  size_t idx = latestBucketIdx;
+  durationStart -= duration_;
+
+  TimeType nextBucketStart =
+    TimeType((scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) +
+    durationStart;
+  while (true) {
+    ++idx;
+    if (idx >= buckets_.size()) {
+      idx = 0;
+      durationStart += duration_;
+      scaledNextBucketStart = duration_.count();
+    } else {
+      scaledNextBucketStart += duration_.count();
+    }
+
+    TimeType bucketStart = nextBucketStart;
+    nextBucketStart = TimeType((scaledNextBucketStart + buckets_.size() - 1) /
+                               buckets_.size()) + durationStart;
+
+    // Should we bother skipping buckets where firstTime_ >= nextBucketStart?
+    // For now we go ahead and invoke the function with these buckets.
+    // sum and count should always be 0 in these buckets.
+
+    DCHECK_LE(bucketStart.count(), latestTime_.count());
+    bool ret = fn(buckets_[idx], bucketStart, nextBucketStart);
+    if (!ret) {
+      break;
+    }
+
+    if (idx == latestBucketIdx) {
+      // all done
+      break;
+    }
+  }
+}
+
+/*
+ * Adjust the input value from the specified bucket to only account
+ * for the desired range.
+ *
+ * For example, if the bucket spans time [10, 20), but we only care about the
+ * range [10, 16), this will return 60% of the input value.
+ */
+template<typename VT, typename TT>
+VT BucketedTimeSeries<VT, TT>::rangeAdjust(
+    TimeType bucketStart, TimeType nextBucketStart,
+    TimeType start, TimeType end, ValueType input) const {
+  // If nextBucketStart is greater than latestTime_, treat nextBucketStart as
+  // if it were latestTime_.  This makes us more accurate when someone is
+  // querying for all of the data up to latestTime_.  Even though latestTime_
+  // may only be partially through the bucket, we don't want to adjust
+  // downwards in this case, because the bucket really only has data up to
+  // latestTime_.
+  if (bucketStart <= latestTime_ && nextBucketStart > latestTime_) {
+    nextBucketStart = latestTime_ + TimeType(1);
+  }
+
+  if (start <= bucketStart && end >= nextBucketStart) {
+    // The bucket is wholly contained in the [start, end) interval
+    return input;
+  }
+
+  TimeType intervalStart = std::max(start, bucketStart);
+  TimeType intervalEnd = std::min(end, nextBucketStart);
+  return input * (intervalEnd - intervalStart) /
+    (nextBucketStart - bucketStart);
+}
+
+template <typename VT, typename TT>
+template <typename Function>
+void BucketedTimeSeries<VT, TT>::forEachBucket(TimeType start, TimeType end,
+                                               Function fn) const {
+  forEachBucket([&start, &end, &fn] (const Bucket& bucket, TimeType bucketStart,
+                                     TimeType nextBucketStart) -> bool {
+    if (start >= nextBucketStart) {
+      return true;
+    }
+    if (end <= bucketStart) {
+      return false;
+    }
+    bool ret = fn(bucket, bucketStart, nextBucketStart);
+    return ret;
+  });
+}
+
+} // folly
+
+#endif // FOLLY_STATS_BUCKETEDTIMESERIES_INL_H_
diff --git a/faux-folly/folly/stats/BucketedTimeSeries.h b/faux-folly/folly/stats/BucketedTimeSeries.h
new file mode 100644
index 0000000..96fe540
--- /dev/null
+++ b/faux-folly/folly/stats/BucketedTimeSeries.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_STATS_BUCKETEDTIMESERIES_H_
+#define FOLLY_STATS_BUCKETEDTIMESERIES_H_
+
+#include <chrono>
+#include <vector>
+
+#include <folly/detail/Stats.h>
+
+namespace folly {
+
+/*
+ * This class represents a bucketed time series which keeps track of values
+ * added in the recent past, and merges these values together into a fixed
+ * number of buckets to keep a lid on memory use if the number of values
+ * added is very large.
+ *
+ * For example, a BucketedTimeSeries() with duration == 60s and 10 buckets
+ * will keep track of 10 6-second buckets, and discard all data added more
+ * than 1 minute ago.  As time ticks by, a 6-second bucket at a time will
+ * be discarded and new data will go into the newly opened bucket.  Internally,
+ * it uses a circular array of buckets that it reuses as time advances.
+ *
+ * This class assumes that time advances forwards.  The window of time tracked
+ * by the timeseries will advance forwards whenever a more recent timestamp is
+ * passed to addValue().  While it is possible to pass old time values to
+ * addValue(), this will never move the time window backwards.  If the old time
+ * value falls outside the tracked window of time, the data point will be
+ * ignored.
+ *
+ * This class is not thread-safe -- use your own synchronization!
+ */
+template <typename VT, typename TT=std::chrono::seconds>
+class BucketedTimeSeries {
+ public:
+  typedef VT ValueType;
+  typedef TT TimeType;
+  typedef detail::Bucket<ValueType> Bucket;
+
+  /*
+   * Create a new BucketedTimeSeries.
+   *
+   * This creates a new BucketedTimeSeries with the specified number of
+   * buckets, storing data for the specified amount of time.
+   *
+   * If the duration is 0, the BucketedTimeSeries will track data forever,
+   * and does not need the rolling buckets.  The numBuckets parameter is
+   * ignored when duration is 0.
+   */
+  BucketedTimeSeries(size_t numBuckets, TimeType duration);
+
+  /*
+   * Adds the value 'val' at time 'now'
+   *
+   * This function expects time to generally move forwards.  The window of time
+   * tracked by this time series will move forwards with time.  If 'now' is
+   * more recent than any time previously seen, addValue() will automatically
+   * call update(now) to advance the time window tracked by this data
+   * structure.
+   *
+   * Values in the recent past may be added to the data structure by passing in
+   * a slightly older value of 'now', as long as this time point still falls
+   * within the tracked duration.  If 'now' is older than the tracked duration
+   * of time, the data point value will be ignored, and addValue() will return
+   * false without doing anything else.
+   *
+   * Returns true on success, or false if now was older than the tracked time
+   * window.
+   */
+  bool addValue(TimeType now, const ValueType& val);
+
+  /*
+   * Adds the value 'val' the given number of 'times' at time 'now'
+   */
+  bool addValue(TimeType now, const ValueType& val, int64_t times);
+
+  /*
+   * Adds the value 'sum' as the sum of 'nsamples' samples
+   */
+  bool addValueAggregated(TimeType now, const ValueType& sum, int64_t nsamples);
+
+  /*
+   * Updates the container to the specified time, doing all the necessary
+   * work to rotate the buckets and remove any stale data points.
+   *
+   * The addValue() methods automatically call update() when adding new data
+   * points.  However, when reading data from the timeseries, you should make
+   * sure to manually call update() before accessing the data.  Otherwise you
+   * may be reading stale data if update() has not been called recently.
+   *
+   * Returns the current bucket index after the update.
+   */
+  size_t update(TimeType now);
+
+  /*
+   * Reset the timeseries to an empty state,
+   * as if no data points have ever been added to it.
+   */
+  void clear();
+
+  /*
+   * Get the latest time that has ever been passed to update() or addValue().
+   *
+   * If no data has ever been added to this timeseries, 0 will be returned.
+   */
+  TimeType getLatestTime() const {
+    return latestTime_;
+  }
+
+  /*
+   * Get the time of the earliest data point stored in this timeseries.
+   *
+   * If no data has ever been added to this timeseries, 0 will be returned.
+   *
+   * If isAllTime() is true, this is simply the time when the first data point
+   * was recorded.
+   *
+   * For non-all-time data, the timestamp reflects the first data point still
+   * remembered.  As new data points are added, old data will be expired.
+   * getEarliestTime() returns the timestamp of the oldest bucket still present
+   * in the timeseries.  This will never be older than (getLatestTime() -
+   * duration()).
+   */
+  TimeType getEarliestTime() const;
+
+  /*
+   * Return the number of buckets.
+   */
+  size_t numBuckets() const {
+    return buckets_.size();
+  }
+
+  /*
+   * Return the maximum duration of data that can be tracked by this
+   * BucketedTimeSeries.
+   */
+  TimeType duration() const {
+    return duration_;
+  }
+
+  /*
+   * Returns true if this BucketedTimeSeries stores data for all-time, without
+   * ever rolling over into new buckets.
+   */
+  bool isAllTime() const {
+    return (duration_ == TimeType(0));
+  }
+
+  /*
+   * Returns true if no calls to update() have been made since the last call to
+   * clear().
+   */
+  bool empty() const {
+    // We set firstTime_ greater than latestTime_ in the constructor and in
+    // clear, so we use this to distinguish if the timeseries is empty.
+    //
+    // Once a data point has been added, latestTime_ will always be greater
+    // than or equal to firstTime_.
+    return firstTime_ > latestTime_;
+  }
+
+  /*
+   * Get the amount of time tracked by this timeseries.
+   *
+   * For an all-time timeseries, this returns the length of time since the
+   * first data point was added to the time series.
+   *
+   * Otherwise, this never returns a value greater than the overall timeseries
+   * duration.  If the first data point was recorded less than a full duration
+   * ago, the time since the first data point is returned.  If a full duration
+   * has elapsed, and we have already thrown away some data, the time since the
+   * oldest bucket is returned.
+   *
+   * For example, say we are tracking 600 seconds worth of data, in 60 buckets.
+   * - If less than 600 seconds have elapsed since the first data point,
+   *   elapsed() returns the total elapsed time so far.
+   * - If more than 600 seconds have elapsed, we have already thrown away some
+   *   data.  However, we throw away a full bucket (10 seconds worth) at once,
+   *   so at any point in time we have from 590 to 600 seconds worth of data.
+   *   elapsed() will therefore return a value between 590 and 600.
+   *
+   * Note that you generally should call update() before calling elapsed(), to
+   * make sure you are not reading stale data.
+   */
+  TimeType elapsed() const;
+
+  /*
+   * Get the amount of time tracked by this timeseries, between the specified
+   * start and end times.
+   *
+   * If the timeseries contains data for the entire time range specified, this
+   * simply returns (end - start).  However, if start is earlier than
+   * getEarliestTime(), this returns (end - getEarliestTime()).
+   */
+  TimeType elapsed(TimeType start, TimeType end) const;
+
+  /*
+   * Return the sum of all the data points currently tracked by this
+   * BucketedTimeSeries.
+   *
+   * Note that you generally should call update() before calling sum(), to
+   * make sure you are not reading stale data.
+   */
+  const ValueType& sum() const {
+    return total_.sum;
+  }
+
+  /*
+   * Return the number of data points currently tracked by this
+   * BucketedTimeSeries.
+   *
+   * Note that you generally should call update() before calling count(), to
+   * make sure you are not reading stale data.
+   */
+  uint64_t count() const {
+    return total_.count;
+  }
+
+  /*
+   * Return the average value (sum / count).
+   *
+   * The return type may be specified to control whether floating-point or
+   * integer division should be performed.
+   *
+   * Note that you generally should call update() before calling avg(), to
+   * make sure you are not reading stale data.
+   */
+  template <typename ReturnType=double>
+  ReturnType avg() const {
+    return total_.template avg<ReturnType>();
+  }
+
+  /*
+   * Return the sum divided by the elapsed time.
+   *
+   * Note that you generally should call update() before calling rate(), to
+   * make sure you are not reading stale data.
+   */
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ReturnType rate() const {
+    return rateHelper<ReturnType, Interval>(total_.sum, elapsed());
+  }
+
+  /*
+   * Return the count divided by the elapsed time.
+   *
+   * The Interval template parameter causes the elapsed time to be converted to
+   * the Interval type before using it.  For example, if Interval is
+   * std::chrono::seconds, the return value will be the count per second.
+   * If Interval is std::chrono::hours, the return value will be the count per
+   * hour.
+   *
+   * Note that you generally should call update() before calling countRate(),
+   * to make sure you are not reading stale data.
+   */
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ReturnType countRate() const {
+    return rateHelper<ReturnType, Interval>(total_.count, elapsed());
+  }
+
+  /*
+   * Estimate the sum of the data points that occurred in the specified time
+   * period.
+   *
+   * The range queried is [start, end).
+   * That is, start is inclusive, and end is exclusive.
+   *
+   * Note that data outside of the timeseries duration will no longer be
+   * available for use in the estimation.  Specifying a start time earlier than
+   * getEarliestTime() will not have much effect, since only data points after
+   * that point in time will be counted.
+   *
+   * Note that the value returned is an estimate, and may not be precise.
+   */
+  ValueType sum(TimeType start, TimeType end) const;
+
+  /*
+   * Estimate the number of data points that occurred in the specified time
+   * period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   */
+  uint64_t count(TimeType start, TimeType end) const;
+
+  /*
+   * Estimate the average value during the specified time period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   */
+  template <typename ReturnType=double>
+  ReturnType avg(TimeType start, TimeType end) const;
+
+  /*
+   * Estimate the rate during the specified time period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   */
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ReturnType rate(TimeType start, TimeType end) const {
+    ValueType intervalSum = sum(start, end);
+    TimeType interval = elapsed(start, end);
+    return rateHelper<ReturnType, Interval>(intervalSum, interval);
+  }
+
+  /*
+   * Estimate the rate of data points being added during the specified time
+   * period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   */
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ReturnType countRate(TimeType start, TimeType end) const {
+    uint64_t intervalCount = count(start, end);
+    TimeType interval = elapsed(start, end);
+    return rateHelper<ReturnType, Interval>(intervalCount, interval);
+  }
+
+  /*
+   * Invoke a function for each bucket.
+   *
+   * The function will take as arguments the bucket index,
+   * the bucket start time, and the start time of the subsequent bucket.
+   *
+   * It should return true to continue iterating through the buckets, and false
+   * to break out of the loop and stop, without calling the function on any
+   * more buckets.
+   *
+   * bool function(const Bucket& bucket, TimeType bucketStart,
+   *               TimeType nextBucketStart)
+   */
+  template <typename Function>
+  void forEachBucket(Function fn) const;
+
+  /*
+   * Get the index for the bucket containing the specified time.
+   *
+   * Note that the index is only valid if this time actually falls within one
+   * of the current buckets.  If you pass in a value more recent than
+   * getLatestTime() or older than (getLatestTime() - elapsed()), the index
+   * returned will not be valid.
+   *
+   * This method may not be called for all-time data.
+   */
+  size_t getBucketIdx(TimeType time) const;
+
+  /*
+   * Get the bucket at the specified index.
+   *
+   * This method may not be called for all-time data.
+   */
+  const Bucket& getBucketByIndex(size_t idx) const {
+    return buckets_[idx];
+  }
+
+  /*
+   * Compute the bucket index that the specified time falls into,
+   * as well as the bucket start time and the next bucket's start time.
+   *
+   * This method may not be called for all-time data.
+   */
+  void getBucketInfo(TimeType time, size_t* bucketIdx,
+                     TimeType* bucketStart, TimeType* nextBucketStart) const;
+
+ private:
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ReturnType rateHelper(ReturnType numerator, TimeType elapsedTime) const {
+    return detail::rateHelper<ReturnType, TimeType, Interval>(numerator,
+                                                              elapsedTime);
+  }
+
+  TimeType getEarliestTimeNonEmpty() const;
+  size_t updateBuckets(TimeType now);
+
+  ValueType rangeAdjust(TimeType bucketStart, TimeType nextBucketStart,
+                        TimeType start, TimeType end,
+                        ValueType input) const;
+
+  template <typename Function>
+  void forEachBucket(TimeType start, TimeType end, Function fn) const;
+
+  TimeType firstTime_;   // time of first update() since clear()/constructor
+  TimeType latestTime_;  // time of last update()
+  TimeType duration_;    // total duration ("window length") of the time series
+
+  Bucket total_;                 // sum and count of everything in time series
+  std::vector<Bucket> buckets_;  // actual buckets of values
+};
+
+} // folly
+
+#endif // FOLLY_STATS_BUCKETEDTIMESERIES_H_
diff --git a/faux-folly/folly/stats/Histogram-defs.h b/faux-folly/folly/stats/Histogram-defs.h
new file mode 100644
index 0000000..925ab28
--- /dev/null
+++ b/faux-folly/folly/stats/Histogram-defs.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_HISTOGRAM_DEFS_H_
+#define FOLLY_HISTOGRAM_DEFS_H_
+
+#include <folly/Conv.h>
+
+#include <glog/logging.h>
+
+namespace folly {
+
+namespace detail {
+
+template <typename T, typename BucketT>
+HistogramBuckets<T, BucketT>::HistogramBuckets(ValueType bucketSize,
+                                               ValueType min,
+                                               ValueType max,
+                                               const BucketType& defaultBucket)
+  : bucketSize_(bucketSize),
+    min_(min),
+    max_(max) {
+  CHECK_GT(bucketSize_, ValueType(0));
+  CHECK_LT(min_, max_);
+
+  // Deliberately make this a signed type, because we're about
+  // to compare it against max-min, which is nominally signed, too.
+  int numBuckets = (max - min) / bucketSize;
+  // Round up if the bucket size does not fit evenly
+  if (numBuckets * bucketSize < max - min) {
+    ++numBuckets;
+  }
+  // Add 2 for the extra 'below min' and 'above max' buckets
+  numBuckets += 2;
+  buckets_.assign(numBuckets, defaultBucket);
+}
+
+template <typename T, typename BucketType>
+unsigned int HistogramBuckets<T, BucketType>::getBucketIdx(
+    ValueType value) const {
+  if (value < min_) {
+    return 0;
+  } else if (value >= max_) {
+    return buckets_.size() - 1;
+  } else {
+    // the 1 is the below_min bucket
+    return ((value - min_) / bucketSize_) + 1;
+  }
+}
+
+template <typename T, typename BucketType>
+template <typename CountFn>
+const uint64_t HistogramBuckets<T, BucketType>::computeTotalCount(
+    CountFn countFromBucket) const {
+  uint64_t count = 0;
+  for (unsigned int n = 0; n < buckets_.size(); ++n) {
+    count += countFromBucket(const_cast<const BucketType&>(buckets_[n]));
+  }
+  return count;
+}
+
+template <typename T, typename BucketType>
+template <typename CountFn>
+unsigned int HistogramBuckets<T, BucketType>::getPercentileBucketIdx(
+    double pct,
+    CountFn countFromBucket,
+    double* lowPct, double* highPct) const {
+  CHECK_GE(pct, 0.0);
+  CHECK_LE(pct, 1.0);
+
+  unsigned int numBuckets = buckets_.size();
+
+  // Compute the counts in each bucket
+  std::vector<uint64_t> counts(numBuckets);
+  uint64_t totalCount = 0;
+  for (unsigned int n = 0; n < numBuckets; ++n) {
+    uint64_t bucketCount =
+      countFromBucket(const_cast<const BucketType&>(buckets_[n]));
+    counts[n] = bucketCount;
+    totalCount += bucketCount;
+  }
+
+  // If there are no elements, just return the lowest bucket.
+  // Note that we return bucket 1, which is the first bucket in the
+  // histogram range; bucket 0 is for all values below min_.
+  if (totalCount == 0) {
+    // Set lowPct and highPct both to 0.
+    // getPercentileEstimate() will recognize this to mean that the histogram
+    // is empty.
+    if (lowPct) {
+      *lowPct = 0.0;
+    }
+    if (highPct) {
+      *highPct = 0.0;
+    }
+    return 1;
+  }
+
+  // Loop through all the buckets, keeping track of each bucket's
+  // percentile range: [0,10], [10,17], [17,45], etc.  When we find a range
+  // that includes our desired percentile, we return that bucket index.
+  double prevPct = 0.0;
+  double curPct = 0.0;
+  uint64_t curCount = 0;
+  unsigned int idx;
+  for (idx = 0; idx < numBuckets; ++idx) {
+    if (counts[idx] == 0) {
+      // skip empty buckets
+      continue;
+    }
+
+    prevPct = curPct;
+    curCount += counts[idx];
+    curPct = static_cast<double>(curCount) / totalCount;
+    if (pct <= curPct) {
+      // This is the desired bucket
+      break;
+    }
+  }
+
+  if (lowPct) {
+    *lowPct = prevPct;
+  }
+  if (highPct) {
+    *highPct = curPct;
+  }
+  return idx;
+}
+
+template <typename T, typename BucketType>
+template <typename CountFn, typename AvgFn>
+T HistogramBuckets<T, BucketType>::getPercentileEstimate(
+    double pct,
+    CountFn countFromBucket,
+    AvgFn avgFromBucket) const {
+
+  // Find the bucket where this percentile falls
+  double lowPct;
+  double highPct;
+  unsigned int bucketIdx = getPercentileBucketIdx(pct, countFromBucket,
+                                                  &lowPct, &highPct);
+  if (lowPct == 0.0 && highPct == 0.0) {
+    // Invalid range -- the buckets must all be empty
+    // Return the default value for ValueType.
+    return ValueType();
+  }
+  if (lowPct == highPct) {
+    // Unlikely to have exact equality,
+    // but just return the bucket average in this case.
+    // We handle this here to avoid division by 0 below.
+    return avgFromBucket(buckets_[bucketIdx]);
+  }
+
+  CHECK_GE(pct, lowPct);
+  CHECK_LE(pct, highPct);
+  CHECK_LT(lowPct, highPct);
+
+  // Compute information about this bucket
+  ValueType avg = avgFromBucket(buckets_[bucketIdx]);
+  ValueType low;
+  ValueType high;
+  if (bucketIdx == 0) {
+    if (avg > min_) {
+      // This normally shouldn't happen.  This bucket is only supposed to track
+      // values less than min_.  Most likely this means that integer overflow
+      // occurred, and the code in avgFromBucket() returned a huge value
+      // instead of a small one.  Just return the minimum possible value for
+      // now.
+      //
+      // (Note that if the counter keeps being decremented, eventually it will
+      // wrap and become small enough that we won't detect this any more, and
+      // we will return bogus information.)
+      LOG(ERROR) << "invalid average value in histogram minimum bucket: " <<
+        avg << " > " << min_ << ": possible integer overflow?";
+      return getBucketMin(bucketIdx);
+    }
+    // For the below-min bucket, just assume the lowest value ever seen is
+    // twice as far away from min_ as avg.
+    high = min_;
+    low = high - (2 * (high - avg));
+    // Adjust low in case it wrapped
+    if (low > avg) {
+      low = std::numeric_limits<ValueType>::min();
+    }
+  } else if (bucketIdx == buckets_.size() - 1) {
+    if (avg < max_) {
+      // Most likely this means integer overflow occurred.  See the comments
+      // above in the minimum case.
+      LOG(ERROR) << "invalid average value in histogram maximum bucket: " <<
+        avg << " < " << max_ << ": possible integer overflow?";
+      return getBucketMax(bucketIdx);
+    }
+    // Similarly for the above-max bucket, assume the highest value ever seen
+    // is twice as far away from max_ as avg.
+    low = max_;
+    high = low + (2 * (avg - low));
+    // Adjust high in case it wrapped
+    if (high < avg) {
+      high = std::numeric_limits<ValueType>::max();
+    }
+  } else {
+    low = getBucketMin(bucketIdx);
+    high = getBucketMax(bucketIdx);
+    if (avg < low || avg > high) {
+      // Most likely this means an integer overflow occurred.
+      // See the comments above.  Return the midpoint between low and high
+      // as a best guess, since avg is meaningless.
+      LOG(ERROR) << "invalid average value in histogram bucket: " <<
+        avg << " not in range [" << low << ", " << high <<
+        "]: possible integer overflow?";
+      return (low + high) / 2;
+    }
+  }
+
+  // Since we know the average value in this bucket, we can do slightly better
+  // than just assuming the data points in this bucket are uniformly
+  // distributed between low and high.
+  //
+  // Assume that the median value in this bucket is the same as the average
+  // value.
+  double medianPct = (lowPct + highPct) / 2.0;
+  if (pct < medianPct) {
+    // Assume that the data points lower than the median of this bucket
+    // are uniformly distributed between low and avg
+    double pctThroughSection = (pct - lowPct) / (medianPct - lowPct);
+    return low + ((avg - low) * pctThroughSection);
+  } else {
+    // Assume that the data points greater than the median of this bucket
+    // are uniformly distributed between avg and high
+    double pctThroughSection = (pct - medianPct) / (highPct - medianPct);
+    return avg + ((high - avg) * pctThroughSection);
+  }
+}
+
+} // detail
+
+
+template <typename T>
+std::string Histogram<T>::debugString() const {
+  std::string ret = folly::to<std::string>(
+      "num buckets: ", buckets_.getNumBuckets(),
+      ", bucketSize: ", buckets_.getBucketSize(),
+      ", min: ", buckets_.getMin(), ", max: ", buckets_.getMax(), "\n");
+
+  for (unsigned int i = 0; i < buckets_.getNumBuckets(); ++i) {
+    folly::toAppend("  ", buckets_.getBucketMin(i), ": ",
+                    buckets_.getByIndex(i).count, "\n",
+                    &ret);
+  }
+
+  return ret;
+}
+
+template <typename T>
+void Histogram<T>::toTSV(std::ostream& out, bool skipEmptyBuckets) const {
+  for (unsigned int i = 0; i < buckets_.getNumBuckets(); ++i) {
+    // Do not output empty buckets in order to reduce data file size.
+    if (skipEmptyBuckets && getBucketByIndex(i).count == 0) {
+      continue;
+    }
+    const auto& bucket = getBucketByIndex(i);
+    out << getBucketMin(i) << '\t' << getBucketMax(i) << '\t'
+        << bucket.count << '\t' << bucket.sum << '\n';
+  }
+}
+
+} // folly
+
+#endif // FOLLY_HISTOGRAM_DEFS_H_
diff --git a/faux-folly/folly/stats/Histogram.h b/faux-folly/folly/stats/Histogram.h
new file mode 100644
index 0000000..7b7ee1e
--- /dev/null
+++ b/faux-folly/folly/stats/Histogram.h
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_HISTOGRAM_H_
+#define FOLLY_HISTOGRAM_H_
+
+#include <cstddef>
+#include <limits>
+#include <ostream>
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+#include <folly/detail/Stats.h>
+
+namespace folly {
+
+namespace detail {
+
+/*
+ * A helper class to manage a set of histogram buckets.
+ */
+template <typename T, typename BucketT>
+class HistogramBuckets {
+ public:
+  typedef T ValueType;
+  typedef BucketT BucketType;
+
+  /*
+   * Create a set of histogram buckets.
+   *
+   * One bucket will be created for each bucketSize interval of values within
+   * the specified range.  Additionally, one bucket will be created to track
+   * all values that fall below the specified minimum, and one bucket will be
+   * created for all values above the specified maximum.
+   *
+   * If (max - min) is not a multiple of bucketSize, the last bucket will cover
+   * a smaller range of values than the other buckets.
+   *
+   * (max - min) must be larger than or equal to bucketSize.
+   */
+  HistogramBuckets(ValueType bucketSize, ValueType min, ValueType max,
+                   const BucketType& defaultBucket);
+
+  /* Returns the bucket size of each bucket in the histogram. */
+  ValueType getBucketSize() const {
+    return bucketSize_;
+  }
+
+  /* Returns the min value at which bucketing begins. */
+  ValueType getMin() const {
+    return min_;
+  }
+
+  /* Returns the max value at which bucketing ends. */
+  ValueType getMax() const {
+    return max_;
+  }
+
+  /*
+   * Returns the number of buckets.
+   *
+   * This includes the total number of buckets for the [min, max) range,
+   * plus 2 extra buckets, one for handling values less than min, and one for
+   * values greater than max.
+   */
+  unsigned int getNumBuckets() const {
+    return buckets_.size();
+  }
+
+  /* Returns the bucket index into which the given value would fall. */
+  unsigned int getBucketIdx(ValueType value) const;
+
+  /* Returns the bucket for the specified value */
+  BucketType& getByValue(ValueType value) {
+    return buckets_[getBucketIdx(value)];
+  }
+
+  /* Returns the bucket for the specified value */
+  const BucketType& getByValue(ValueType value) const {
+    return buckets_[getBucketIdx(value)];
+  }
+
+  /*
+   * Returns the bucket at the specified index.
+   *
+   * Note that index 0 is the bucket for all values less than the specified
+   * minimum.  Index 1 is the first bucket in the specified bucket range.
+   */
+  BucketType& getByIndex(unsigned int idx) {
+    return buckets_[idx];
+  }
+
+  /* Returns the bucket at the specified index. */
+  const BucketType& getByIndex(unsigned int idx) const {
+    return buckets_[idx];
+  }
+
+  /*
+   * Returns the minimum threshold for the bucket at the given index.
+   *
+   * The bucket at the specified index will store values in the range
+   * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall
+   * max is smaller than bucketMin + bucketSize.
+   */
+  ValueType getBucketMin(unsigned int idx) const {
+    if (idx == 0) {
+      return std::numeric_limits<ValueType>::min();
+    }
+    if (idx == buckets_.size() - 1) {
+      return max_;
+    }
+
+    return min_ + ((idx - 1) * bucketSize_);
+  }
+
+  /*
+   * Returns the maximum threshold for the bucket at the given index.
+   *
+   * The bucket at the specified index will store values in the range
+   * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall
+   * max is smaller than bucketMin + bucketSize.
+   */
+  ValueType getBucketMax(unsigned int idx) const {
+    if (idx == buckets_.size() - 1) {
+      return std::numeric_limits<ValueType>::max();
+    }
+
+    return min_ + (idx * bucketSize_);
+  }
+
+  /**
+   * Computes the total number of values stored across all buckets.
+   *
+   * Runs in O(numBuckets)
+   *
+   * @param countFn A function that takes a const BucketType&, and returns the
+   *                number of values in that bucket
+   * @return Returns the total number of values stored across all buckets
+   */
+  template <typename CountFn>
+  const uint64_t computeTotalCount(CountFn countFromBucket) const;
+
+  /**
+   * Determine which bucket the specified percentile falls into.
+   *
+   * Looks for the bucket that contains the Nth percentile data point.
+   *
+   * @param pct     The desired percentile to find, as a value from 0.0 to 1.0.
+   * @param countFn A function that takes a const BucketType&, and returns the
+   *                number of values in that bucket.
+   * @param lowPct  The lowest percentile stored in the selected bucket will be
+   *                returned via this parameter.
+   * @param highPct The highest percentile stored in the selected bucket will
+   *                be returned via this parameter.
+   *
+   * @return Returns the index of the bucket that contains the Nth percentile
+   *         data point.
+   */
+  template <typename CountFn>
+  unsigned int getPercentileBucketIdx(double pct,
+                                      CountFn countFromBucket,
+                                      double* lowPct = nullptr,
+                                      double* highPct = nullptr) const;
+
+  /**
+   * Estimate the value at the specified percentile.
+   *
+   * @param pct     The desired percentile to find, as a value from 0.0 to 1.0.
+   * @param countFn A function that takes a const BucketType&, and returns the
+   *                number of values in that bucket.
+   * @param avgFn   A function that takes a const BucketType&, and returns the
+   *                average of all the values in that bucket.
+   *
+   * @return Returns an estimate for N, where N is the number where exactly pct
+   *         percentage of the data points in the histogram are less than N.
+   */
+  template <typename CountFn, typename AvgFn>
+  ValueType getPercentileEstimate(double pct,
+                                  CountFn countFromBucket,
+                                  AvgFn avgFromBucket) const;
+
+  /*
+   * Iterator access to the buckets.
+   *
+   * Note that the first bucket is for all values less than min, and the last
+   * bucket is for all values greater than max.  The buckets tracking values in
+   * the [min, max) actually start at the second bucket.
+   */
+  typename std::vector<BucketType>::const_iterator begin() const {
+    return buckets_.begin();
+  }
+  typename std::vector<BucketType>::iterator begin() {
+    return buckets_.begin();
+  }
+  typename std::vector<BucketType>::const_iterator end() const {
+    return buckets_.end();
+  }
+  typename std::vector<BucketType>::iterator end() {
+    return buckets_.end();
+  }
+
+ private:
+  ValueType bucketSize_;
+  ValueType min_;
+  ValueType max_;
+  std::vector<BucketType> buckets_;
+};
+
+} // detail
+
+
+/*
+ * A basic histogram class.
+ *
+ * Groups data points into equally-sized buckets, and stores the overall sum of
+ * the data points in each bucket, as well as the number of data points in the
+ * bucket.
+ *
+ * The caller must specify the minimum and maximum data points to expect ahead
+ * of time, as well as the bucket width.
+ */
+template <typename T>
+class Histogram {
+ public:
+  typedef T ValueType;
+  typedef detail::Bucket<T> Bucket;
+
+  Histogram(ValueType bucketSize, ValueType min, ValueType max)
+    : buckets_(bucketSize, min, max, Bucket()) {}
+
+  /* Add a data point to the histogram */
+  void addValue(ValueType value) {
+    Bucket& bucket = buckets_.getByValue(value);
+    // TODO: It would be nice to handle overflow here.
+    bucket.sum += value;
+    bucket.count += 1;
+  }
+
+  /* Add multiple same data points to the histogram */
+  void addRepeatedValue(ValueType value, uint64_t nSamples) {
+    Bucket& bucket = buckets_.getByValue(value);
+    // TODO: It would be nice to handle overflow here.
+    bucket.sum += value * nSamples;
+    bucket.count += nSamples;
+  }
+
+  /*
+   * Remove a data point to the histogram
+   *
+   * Note that this method does not actually verify that this exact data point
+   * had previously been added to the histogram; it merely subtracts the
+   * requested value from the appropriate bucket's sum.
+   */
+  void removeValue(ValueType value) {
+    Bucket& bucket = buckets_.getByValue(value);
+    // TODO: It would be nice to handle overflow here.
+    if (bucket.count > 0) {
+      bucket.sum -= value;
+      bucket.count -= 1;
+    } else {
+      bucket.sum = ValueType();
+      bucket.count = 0;
+    }
+  }
+
+  /* Remove multiple same data points from the histogram */
+  void removeRepeatedValue(ValueType value, uint64_t nSamples) {
+    Bucket& bucket = buckets_.getByValue(value);
+    // TODO: It would be nice to handle overflow here.
+    if (bucket.count >= nSamples) {
+      bucket.sum -= value * nSamples;
+      bucket.count -= nSamples;
+    } else {
+      bucket.sum = ValueType();
+      bucket.count = 0;
+    }
+  }
+
+  /* Remove all data points from the histogram */
+  void clear() {
+    for (unsigned int i = 0; i < buckets_.getNumBuckets(); i++) {
+      buckets_.getByIndex(i).clear();
+    }
+  }
+
+  /* Subtract another histogram data from the histogram */
+  void subtract(const Histogram &hist) {
+    // the two histogram bucket definitions must match to support
+    // subtract.
+    if (getBucketSize() != hist.getBucketSize() ||
+        getMin() != hist.getMin() ||
+        getMax() != hist.getMax() ||
+        getNumBuckets() != hist.getNumBuckets() ) {
+      throw std::invalid_argument("Cannot subtract input histogram.");
+    }
+
+    for (unsigned int i = 0; i < buckets_.getNumBuckets(); i++) {
+      buckets_.getByIndex(i) -= hist.buckets_.getByIndex(i);
+    }
+  }
+
+  /* Merge two histogram data together */
+  void merge(const Histogram &hist) {
+    // the two histogram bucket definitions must match to support
+    // a merge.
+    if (getBucketSize() != hist.getBucketSize() ||
+        getMin() != hist.getMin() ||
+        getMax() != hist.getMax() ||
+        getNumBuckets() != hist.getNumBuckets() ) {
+      throw std::invalid_argument("Cannot merge from input histogram.");
+    }
+
+    for (unsigned int i = 0; i < buckets_.getNumBuckets(); i++) {
+      buckets_.getByIndex(i) += hist.buckets_.getByIndex(i);
+    }
+  }
+
+  /* Copy bucket values from another histogram */
+  void copy(const Histogram &hist) {
+    // the two histogram bucket definition must match
+    if (getBucketSize() != hist.getBucketSize() ||
+        getMin() != hist.getMin() ||
+        getMax() != hist.getMax() ||
+        getNumBuckets() != hist.getNumBuckets() ) {
+      throw std::invalid_argument("Cannot copy from input histogram.");
+    }
+
+    for (unsigned int i = 0; i < buckets_.getNumBuckets(); i++) {
+      buckets_.getByIndex(i) = hist.buckets_.getByIndex(i);
+    }
+  }
+
+  /* Returns the bucket size of each bucket in the histogram. */
+  ValueType getBucketSize() const {
+    return buckets_.getBucketSize();
+  }
+  /* Returns the min value at which bucketing begins. */
+  ValueType getMin() const {
+    return buckets_.getMin();
+  }
+  /* Returns the max value at which bucketing ends. */
+  ValueType getMax() const {
+    return buckets_.getMax();
+  }
+  /* Returns the number of buckets */
+  unsigned int getNumBuckets() const {
+    return buckets_.getNumBuckets();
+  }
+
+  /* Returns the specified bucket (for reading only!) */
+  const Bucket& getBucketByIndex(int idx) const {
+    return buckets_.getByIndex(idx);
+  }
+
+  /*
+   * Returns the minimum threshold for the bucket at the given index.
+   *
+   * The bucket at the specified index will store values in the range
+   * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall
+   * max is smaller than bucketMin + bucketSize.
+   */
+  ValueType getBucketMin(unsigned int idx) const {
+    return buckets_.getBucketMin(idx);
+  }
+
+  /*
+   * Returns the maximum threshold for the bucket at the given index.
+   *
+   * The bucket at the specified index will store values in the range
+   * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall
+   * max is smaller than bucketMin + bucketSize.
+   */
+  ValueType getBucketMax(unsigned int idx) const {
+    return buckets_.getBucketMax(idx);
+  }
+
+  /**
+   * Computes the total number of values stored across all buckets.
+   *
+   * Runs in O(numBuckets)
+   */
+  const uint64_t computeTotalCount() const {
+    CountFromBucket countFn;
+    return buckets_.computeTotalCount(countFn);
+  }
+
+  /*
+   * Get the bucket that the specified percentile falls into
+   *
+   * The lowest and highest percentile data points in returned bucket will be
+   * returned in the lowPct and highPct arguments, if they are non-NULL.
+   */
+  unsigned int getPercentileBucketIdx(double pct,
+                                      double* lowPct = nullptr,
+                                      double* highPct = nullptr) const {
+    // We unfortunately can't use lambdas here yet;
+    // Some users of this code are still built with gcc-4.4.
+    CountFromBucket countFn;
+    return buckets_.getPercentileBucketIdx(pct, countFn, lowPct, highPct);
+  }
+
+  /**
+   * Estimate the value at the specified percentile.
+   *
+   * @param pct     The desired percentile to find, as a value from 0.0 to 1.0.
+   *
+   * @return Returns an estimate for N, where N is the number where exactly pct
+   *         percentage of the data points in the histogram are less than N.
+   */
+  ValueType getPercentileEstimate(double pct) const {
+    CountFromBucket countFn;
+    AvgFromBucket avgFn;
+    return buckets_.getPercentileEstimate(pct, countFn, avgFn);
+  }
+
+  /*
+   * Get a human-readable string describing the histogram contents
+   */
+  std::string debugString() const;
+
+  /*
+   * Write the histogram contents in tab-separated values (TSV) format.
+   * Format is "min max count sum".
+   */
+  void toTSV(std::ostream& out, bool skipEmptyBuckets = true) const;
+
+  struct CountFromBucket {
+    uint64_t operator()(const Bucket& bucket) const {
+      return bucket.count;
+    }
+  };
+  struct AvgFromBucket {
+    ValueType operator()(const Bucket& bucket) const {
+      if (bucket.count == 0) {
+        return ValueType(0);
+      }
+      // Cast bucket.count to a signed integer type.  This ensures that we
+      // perform division properly here: If bucket.sum is a signed integer
+      // type but we divide by an unsigned number, unsigned division will be
+      // performed and bucket.sum will be converted to unsigned first.
+      // If bucket.sum is unsigned, the code will still do unsigned division
+      // correctly.
+      //
+      // The only downside is if bucket.count is large enough to be negative
+      // when treated as signed.  That should be extremely unlikely, though.
+      return bucket.sum / static_cast<int64_t>(bucket.count);
+    }
+  };
+
+ private:
+  detail::HistogramBuckets<ValueType, Bucket> buckets_;
+};
+
+} // folly
+
+#endif // FOLLY_HISTOGRAM_H_
diff --git a/faux-folly/folly/stats/Instantiations.cpp b/faux-folly/folly/stats/Instantiations.cpp
new file mode 100644
index 0000000..e6051a1
--- /dev/null
+++ b/faux-folly/folly/stats/Instantiations.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ * This file contains explicit instantiations of stats template types.
+ *
+ * This allows most users to avoid having to include the template definition
+ * header files.
+ */
+
+#include <folly/stats/BucketedTimeSeries.h>
+#include <folly/stats/BucketedTimeSeries-defs.h>
+
+#include <folly/stats/Histogram.h>
+#include <folly/stats/Histogram-defs.h>
+
+#include <folly/stats/MultiLevelTimeSeries.h>
+#include <folly/stats/MultiLevelTimeSeries-defs.h>
+
+#include <folly/stats/TimeseriesHistogram.h>
+#include <folly/stats/TimeseriesHistogram-defs.h>
+
+namespace folly {
+
+template class BucketedTimeSeries<int64_t>;
+template class Histogram<int64_t>;
+template class detail::HistogramBuckets<int64_t, Histogram<int64_t>::Bucket>;
+template class MultiLevelTimeSeries<int64_t>;
+template class TimeseriesHistogram<int64_t>;
+
+// Histogram::getPercentileBucketIdx() and Histogram::getPercentileEstimate()
+// are implemented using template methods.  Instantiate the default versions of
+// these methods too, so anyone using them won't also need to explicitly
+// include Histogram-defs.h
+template unsigned int detail::HistogramBuckets<
+  int64_t, Histogram<int64_t>::Bucket>::
+  getPercentileBucketIdx<Histogram<int64_t>::CountFromBucket>(
+      double pct,
+      Histogram<int64_t>::CountFromBucket countFromBucket,
+      double* lowPct,
+      double* highPct) const;
+template int64_t detail::HistogramBuckets<int64_t, Histogram<int64_t>::Bucket>
+  ::getPercentileEstimate<Histogram<int64_t>::CountFromBucket,
+                          Histogram<int64_t>::AvgFromBucket>(
+    double pct,
+    Histogram<int64_t>::CountFromBucket countFromBucket,
+    Histogram<int64_t>::AvgFromBucket avgFromBucket) const;
+
+} // folly
diff --git a/faux-folly/folly/stats/MultiLevelTimeSeries-defs.h b/faux-folly/folly/stats/MultiLevelTimeSeries-defs.h
new file mode 100644
index 0000000..99bc380
--- /dev/null
+++ b/faux-folly/folly/stats/MultiLevelTimeSeries-defs.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_STATS_MULTILEVELTIMESERIES_DEFS_H_
+#define FOLLY_STATS_MULTILEVELTIMESERIES_DEFS_H_
+
+#include <glog/logging.h>
+
+namespace folly {
+
+template <typename VT, typename TT>
+MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries(
+  size_t nBuckets,
+  size_t nLevels,
+  const TimeType levelDurations[])
+    : cachedTime_(0),
+      cachedSum_(0),
+      cachedCount_(0) {
+    CHECK_GT(nLevels, 0);
+    CHECK(levelDurations);
+
+    levels_.reserve(nLevels);
+    for (size_t i = 0; i < nLevels; ++i) {
+      if (levelDurations[i] == TT(0)) {
+        CHECK_EQ(i, nLevels - 1);
+      } else if (i > 0) {
+        CHECK(levelDurations[i-1] < levelDurations[i]);
+      }
+      levels_.emplace_back(nBuckets, levelDurations[i]);
+    }
+}
+
+template <typename VT, typename TT>
+void MultiLevelTimeSeries<VT, TT>::addValue(TimeType now,
+                                            const ValueType& val) {
+  addValueAggregated(now, val, 1);
+}
+
+template <typename VT, typename TT>
+void MultiLevelTimeSeries<VT, TT>::addValue(TimeType now,
+                                            const ValueType& val,
+                                            int64_t times) {
+  addValueAggregated(now, val * times, times);
+}
+
+template <typename VT, typename TT>
+void MultiLevelTimeSeries<VT, TT>::addValueAggregated(TimeType now,
+                                                      const ValueType& total,
+                                                      int64_t nsamples) {
+  if (cachedTime_ != now) {
+    flush();
+    cachedTime_ = now;
+  }
+  cachedSum_ += total;
+  cachedCount_ += nsamples;
+}
+
+template <typename VT, typename TT>
+void MultiLevelTimeSeries<VT, TT>::update(TimeType now) {
+  flush();
+  for (size_t i = 0; i < levels_.size(); ++i) {
+    levels_[i].update(now);
+  }
+}
+
+template <typename VT, typename TT>
+void MultiLevelTimeSeries<VT, TT>::flush() {
+  // update all the underlying levels
+  if (cachedCount_ > 0) {
+    for (size_t i = 0; i < levels_.size(); ++i) {
+      levels_[i].addValueAggregated(cachedTime_, cachedSum_, cachedCount_);
+    }
+    cachedCount_ = 0;
+    cachedSum_ = 0;
+  }
+}
+
+template <typename VT, typename TT>
+void MultiLevelTimeSeries<VT, TT>::clear() {
+  for (auto & level : levels_) {
+    level.clear();
+  }
+
+  cachedTime_ = TimeType(0);
+  cachedSum_ = 0;
+  cachedCount_ = 0;
+}
+
+}  // folly
+
+#endif // FOLLY_STATS_MULTILEVELTIMESERIES_DEFS_H_
diff --git a/faux-folly/folly/stats/MultiLevelTimeSeries.h b/faux-folly/folly/stats/MultiLevelTimeSeries.h
new file mode 100644
index 0000000..81990d0
--- /dev/null
+++ b/faux-folly/folly/stats/MultiLevelTimeSeries.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_STATS_MULTILEVELTIMESERIES_H_
+#define FOLLY_STATS_MULTILEVELTIMESERIES_H_
+
+#include <chrono>
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+#include <folly/stats/BucketedTimeSeries.h>
+
+namespace folly {
+
+/*
+ * This class represents a timeseries which keeps several levels of data
+ * granularity (similar in principle to the loads reported by the UNIX
+ * 'uptime' command).  It uses several instances (one per level) of
+ * BucketedTimeSeries as the underlying storage.
+ *
+ * This can easily be used to track sums (and thus rates or averages) over
+ * several predetermined time periods, as well as all-time sums.  For example,
+ * you would use to it to track query rate or response speed over the last
+ * 5, 15, 30, and 60 minutes.
+ *
+ * The MultiLevelTimeSeries takes a list of level durations as an input; the
+ * durations must be strictly increasing.  Furthermore a special level can be
+ * provided with a duration of '0' -- this will be an "all-time" level.  If
+ * an all-time level is provided, it MUST be the last level present.
+ *
+ * The class assumes that time advances forward --  you can't retroactively add
+ * values for events in the past -- the 'now' argument is provided for better
+ * efficiency and ease of unittesting.
+ *
+ * The class is not thread-safe -- use your own synchronization!
+ */
+template <typename VT, typename TT=std::chrono::seconds>
+class MultiLevelTimeSeries {
+ public:
+  typedef VT ValueType;
+  typedef TT TimeType;
+  typedef folly::BucketedTimeSeries<ValueType, TimeType> Level;
+
+  /*
+   * Create a new MultiLevelTimeSeries.
+   *
+   * This creates a new MultiLevelTimeSeries that tracks time series data at the
+   * specified time durations (level). The time series data tracked at each
+   * level is then further divided by numBuckets for memory efficiency.
+   *
+   * The durations must be strictly increasing. Furthermore a special level can
+   * be provided with a duration of '0' -- this will be an "all-time" level. If
+   * an all-time level is provided, it MUST be the last level present.
+   */
+  MultiLevelTimeSeries(size_t numBuckets,
+                       size_t numLevels,
+                       const TimeType levelDurations[]);
+
+  /*
+   * Return the number of buckets used to track time series at each level.
+   */
+  size_t numBuckets() const {
+    // The constructor ensures that levels_ has at least one item
+    return levels_[0].numBuckets();
+  }
+
+  /*
+   * Return the number of levels tracked by MultiLevelTimeSeries.
+   */
+  size_t numLevels() const { return levels_.size(); }
+
+  /*
+   * Get the BucketedTimeSeries backing the specified level.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  const Level& getLevel(int level) const {
+    CHECK(level >= 0);
+    CHECK_LT(level, levels_.size());
+    return levels_[level];
+  }
+
+  /*
+   * Get the highest granularity level that is still large enough to contain
+   * data going back to the specified start time.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  const Level& getLevel(TimeType start) const {
+    for (const auto& level : levels_) {
+      if (level.isAllTime()) {
+        return level;
+      }
+      // Note that we use duration() here rather than elapsed().
+      // If duration is large enough to contain the start time then this level
+      // is good enough, even if elapsed() indicates that no data was recorded
+      // before the specified start time.
+      if (level.getLatestTime() - level.duration() <= start) {
+        return level;
+      }
+    }
+    // We should always have an all-time level, so this is never reached.
+    LOG(FATAL) << "No level of timeseries covers internval"
+               << " from " << start.count() << " to now";
+    return levels_.back();
+  }
+
+  /*
+   * Return the sum of all the data points currently tracked at this level.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  ValueType sum(int level) const {
+    return getLevel(level).sum();
+  }
+
+  /*
+   * Return the average (sum / count) of all the data points currently tracked
+   * at this level.
+   *
+   * The return type may be specified to control whether floating-point or
+   * integer division should be performed.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  template <typename ReturnType=double>
+  ReturnType avg(int level) const {
+    return getLevel(level).template avg<ReturnType>();
+  }
+
+  /*
+   * Return the rate (sum divided by elaspsed time) of the all data points
+   * currently tracked at this level.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ValueType rate(int level) const {
+    return getLevel(level).template rate<ReturnType, Interval>();
+  }
+
+  /*
+   * Return the number of data points currently tracked at this level.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  int64_t count(int level) const {
+    return getLevel(level).count();
+  }
+
+  /*
+   * Return the count divided by the elapsed time tracked at this level.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  template <typename ReturnType=double, typename Interval=TimeType>
+  ReturnType countRate(int level) const {
+    return getLevel(level).template countRate<ReturnType, Interval>();
+  }
+
+  /*
+   * Estimate the sum of the data points that occurred in the specified time
+   * period at this level.
+   *
+   * The range queried is [start, end).
+   * That is, start is inclusive, and end is exclusive.
+   *
+   * Note that data outside of the timeseries duration will no longer be
+   * available for use in the estimation.  Specifying a start time earlier than
+   * getEarliestTime() will not have much effect, since only data points after
+   * that point in time will be counted.
+   *
+   * Note that the value returned is an estimate, and may not be precise.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  ValueType sum(TimeType start, TimeType end) const {
+    return getLevel(start).sum(start, end);
+  }
+
+  /*
+   * Estimate the average value during the specified time period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  template <typename ReturnType=double>
+  ReturnType avg(TimeType start, TimeType end) const {
+    return getLevel(start).template avg<ReturnType>(start, end);
+  }
+
+  /*
+   * Estimate the rate during the specified time period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  template <typename ReturnType=double>
+  ReturnType rate(TimeType start, TimeType end) const {
+    return getLevel(start).template rate<ReturnType>(start, end);
+  }
+
+  /*
+   * Estimate the count during the specified time period.
+   *
+   * The same caveats documented in the sum(TimeType start, TimeType end)
+   * comments apply here as well.
+   *
+   * Note: you should generally call update() or flush() before accessing the
+   * data. Otherwise you may be reading stale data if update() or flush() has
+   * not been called recently.
+   */
+  int64_t count(TimeType start, TimeType end) const {
+    return getLevel(start).count(start, end);
+  }
+
+  /*
+   * Adds the value 'val' at time 'now' to all levels.
+   *
+   * Data points added at the same time point is cached internally here and not
+   * propagated to the underlying levels until either flush() is called or when
+   * update from a different time comes.
+   *
+   * This function expects time to always move forwards: it cannot be used to
+   * add historical data points that have occurred in the past.  If now is
+   * older than the another timestamp that has already been passed to
+   * addValue() or update(), now will be ignored and the latest timestamp will
+   * be used.
+   */
+  void addValue(TimeType now, const ValueType& val);
+
+  /*
+   * Adds the value 'val' at time 'now' to all levels.
+   */
+  void addValue(TimeType now, const ValueType& val, int64_t times);
+
+  /*
+   * Adds the value 'val' at time 'now' to all levels as the sum of 'nsamples'
+   * samples.
+   */
+  void addValueAggregated(TimeType now, const ValueType& sum, int64_t nsamples);
+
+  /*
+   * Update all the levels to the specified time, doing all the necessary
+   * work to rotate the buckets and remove any stale data points.
+   *
+   * When reading data from the timeseries, you should make sure to manually
+   * call update() before accessing the data. Otherwise you may be reading
+   * stale data if update() has not been called recently.
+   */
+  void update(TimeType now);
+
+  /*
+   * Reset all the timeseries to an empty state as if no data points have ever
+   * been added to it.
+   */
+  void clear();
+
+  /*
+   * Flush all cached updates.
+   */
+  void flush();
+
+ private:
+  std::vector<Level> levels_;
+
+  // Updates within the same time interval are cached
+  // They are flushed out when updates from a different time comes,
+  // or flush() is called.
+  TimeType cachedTime_;
+  ValueType cachedSum_;
+  int cachedCount_;
+};
+
+} // folly
+
+#endif // FOLLY_STATS_MULTILEVELTIMESERIES_H_
diff --git a/faux-folly/folly/stats/TimeseriesHistogram-defs.h b/faux-folly/folly/stats/TimeseriesHistogram-defs.h
new file mode 100644
index 0000000..c20b682
--- /dev/null
+++ b/faux-folly/folly/stats/TimeseriesHistogram-defs.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_TIMESERIES_HISTOGRAM_DEF_H_
+#define FOLLY_TIMESERIES_HISTOGRAM_DEF_H_
+
+#include <folly/Conv.h>
+#include <folly/stats/Histogram-defs.h>
+#include <folly/stats/MultiLevelTimeSeries-defs.h>
+#include <folly/stats/BucketedTimeSeries-defs.h>
+
+namespace folly {
+
+template <class T, class TT, class C>
+template <typename ReturnType>
+ReturnType TimeseriesHistogram<T, TT, C>::avg(int level) const {
+  ValueType total = ValueType();
+  int64_t nsamples = 0;
+  for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+    const auto& levelObj = buckets_.getByIndex(b).getLevel(level);
+    total += levelObj.sum();
+    nsamples += levelObj.count();
+  }
+  return folly::detail::avgHelper<ReturnType>(total, nsamples);
+}
+
+template <class T, class TT, class C>
+template <typename ReturnType>
+ReturnType TimeseriesHistogram<T, TT, C>::avg(TimeType start,
+                                              TimeType end) const {
+  ValueType total = ValueType();
+  int64_t nsamples = 0;
+  for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+    const auto& levelObj = buckets_.getByIndex(b).getLevel(start, end);
+    total += levelObj.sum(start, end);
+    nsamples += levelObj.count(start, end);
+  }
+  return folly::detail::avgHelper<ReturnType>(total, nsamples);
+}
+
+template <class T, class TT, class C>
+template <typename ReturnType>
+ReturnType TimeseriesHistogram<T, TT, C>::rate(TimeType start,
+                                               TimeType end) const {
+  ValueType total = ValueType();
+  TimeType elapsed(0);
+  for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+    const auto& level = buckets_.getByIndex(b).getLevel(start);
+    total += level.sum(start, end);
+    elapsed = std::max(elapsed, level.elapsed(start, end));
+  }
+  return folly::detail::rateHelper<ReturnType, TimeType, TimeType>(
+      total, elapsed);
+}
+
+template <typename T, typename TT, typename C>
+TimeseriesHistogram<T, TT, C>::TimeseriesHistogram(ValueType bucketSize,
+                                            ValueType min,
+                                            ValueType max,
+                                            const ContainerType& copyMe)
+  : buckets_(bucketSize, min, max, copyMe),
+    haveNotSeenValue_(true),
+    singleUniqueValue_(false) {
+}
+
+template <typename T, typename TT, typename C>
+void TimeseriesHistogram<T, TT, C>::addValue(TimeType now,
+                                             const ValueType& value) {
+  buckets_.getByValue(value).addValue(now, value);
+  maybeHandleSingleUniqueValue(value);
+}
+
+template <typename T, typename TT, typename C>
+void TimeseriesHistogram<T, TT, C>::addValue(TimeType now,
+                                      const ValueType& value,
+                                      int64_t times) {
+  buckets_.getByValue(value).addValue(now, value, times);
+  maybeHandleSingleUniqueValue(value);
+}
+
+template <typename T, typename TT, typename C>
+void TimeseriesHistogram<T, TT, C>::addValues(
+    TimeType now, const folly::Histogram<ValueType>& hist) {
+  CHECK_EQ(hist.getMin(), getMin());
+  CHECK_EQ(hist.getMax(), getMax());
+  CHECK_EQ(hist.getBucketSize(), getBucketSize());
+  CHECK_EQ(hist.getNumBuckets(), getNumBuckets());
+
+  for (unsigned int n = 0; n < hist.getNumBuckets(); ++n) {
+    const typename folly::Histogram<ValueType>::Bucket& histBucket =
+      hist.getBucketByIndex(n);
+    Bucket& myBucket = buckets_.getByIndex(n);
+    myBucket.addValueAggregated(now, histBucket.sum, histBucket.count);
+  }
+
+  // We don't bother with the singleUniqueValue_ tracking.
+  haveNotSeenValue_ = false;
+  singleUniqueValue_ = false;
+}
+
+template <typename T, typename TT, typename C>
+void TimeseriesHistogram<T, TT, C>::maybeHandleSingleUniqueValue(
+  const ValueType& value) {
+  if (haveNotSeenValue_) {
+    firstValue_ = value;
+    singleUniqueValue_ = true;
+    haveNotSeenValue_ = false;
+  } else if (singleUniqueValue_) {
+    if (value != firstValue_) {
+      singleUniqueValue_ = false;
+    }
+  }
+}
+
+template <typename T, typename TT, typename C>
+T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct,
+                                                       int level) const {
+  if (singleUniqueValue_) {
+    return firstValue_;
+  }
+
+  return buckets_.getPercentileEstimate(pct / 100.0, CountFromLevel(level),
+                                        AvgFromLevel(level));
+}
+
+template <typename T, typename TT, typename C>
+T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct,
+                                                TimeType start,
+                                                TimeType end) const {
+  if (singleUniqueValue_) {
+    return firstValue_;
+  }
+
+  return buckets_.getPercentileEstimate(pct / 100.0,
+                                        CountFromInterval(start, end),
+                                        AvgFromInterval<T>(start, end));
+}
+
+template <typename T, typename TT, typename C>
+int TimeseriesHistogram<T, TT, C>::getPercentileBucketIdx(
+  double pct,
+  int level
+) const {
+  return buckets_.getPercentileBucketIdx(pct / 100.0, CountFromLevel(level));
+}
+
+template <typename T, typename TT, typename C>
+int TimeseriesHistogram<T, TT, C>::getPercentileBucketIdx(double pct,
+                                                   TimeType start,
+                                                   TimeType end) const {
+  return buckets_.getPercentileBucketIdx(pct / 100.0,
+                                         CountFromInterval(start, end));
+}
+
+template <typename T, typename TT, typename C>
+T TimeseriesHistogram<T, TT, C>::rate(int level) const {
+  ValueType total = ValueType();
+  TimeType elapsed(0);
+  for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+    const auto& levelObj = buckets_.getByIndex(b).getLevel(level);
+    total += levelObj.sum();
+    elapsed = std::max(elapsed, levelObj.elapsed());
+  }
+  return elapsed == TimeType(0) ? 0 : (total / elapsed.count());
+}
+
+template <typename T, typename TT, typename C>
+void TimeseriesHistogram<T, TT, C>::clear() {
+  for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
+    buckets_.getByIndex(i).clear();
+  }
+}
+
+template <typename T, typename TT, typename C>
+void TimeseriesHistogram<T, TT, C>::update(TimeType now) {
+  for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
+    buckets_.getByIndex(i).update(now);
+  }
+}
+
+template <typename T, typename TT, typename C>
+std::string TimeseriesHistogram<T, TT, C>::getString(int level) const {
+  std::string result;
+
+  for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
+    if (i > 0) {
+      toAppend(",", &result);
+    }
+    const ContainerType& cont = buckets_.getByIndex(i);
+    toAppend(buckets_.getBucketMin(i),
+             ":", cont.count(level),
+             ":", cont.template avg<ValueType>(level), &result);
+  }
+
+  return result;
+}
+
+template <typename T, typename TT, typename C>
+std::string TimeseriesHistogram<T, TT, C>::getString(TimeType start,
+                                                     TimeType end) const {
+  std::string result;
+
+  for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
+    if (i > 0) {
+      toAppend(",", &result);
+    }
+    const ContainerType& cont = buckets_.getByIndex(i);
+    toAppend(buckets_.getBucketMin(i),
+             ":", cont.count(start, end),
+             ":", cont.avg(start, end), &result);
+  }
+
+  return result;
+}
+
+}  // namespace folly
+
+#endif
diff --git a/faux-folly/folly/stats/TimeseriesHistogram.h b/faux-folly/folly/stats/TimeseriesHistogram.h
new file mode 100644
index 0000000..37a3bb0
--- /dev/null
+++ b/faux-folly/folly/stats/TimeseriesHistogram.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_TIMESERIES_HISTOGRAM_H_
+#define FOLLY_TIMESERIES_HISTOGRAM_H_
+
+#include <string>
+#include <boost/static_assert.hpp>
+#include <folly/stats/Histogram.h>
+#include <folly/stats/MultiLevelTimeSeries.h>
+
+namespace folly {
+
+/*
+ * TimeseriesHistogram tracks data distributions as they change over time.
+ *
+ * Specifically, it is a bucketed histogram with different value ranges assigned
+ * to each bucket.  Within each bucket is a MultiLevelTimeSeries from
+ * 'folly/stats/MultiLevelTimeSeries.h'. This means that each bucket contains a
+ * different set of data for different historical time periods, and one can
+ * query data distributions over different trailing time windows.
+ *
+ * For example, this can answer questions: "What is the data distribution over
+ * the last minute? Over the last 10 minutes?  Since I last cleared this
+ * histogram?"
+ *
+ * The class can also estimate percentiles and answer questions like: "What was
+ * the 99th percentile data value over the last 10 minutes?"
+ *
+ * Note: that depending on the size of your buckets and the smoothness
+ * of your data distribution, the estimate may be way off from the actual
+ * value.  In particular, if the given percentile falls outside of the bucket
+ * range (i.e. your buckets range in 0 - 100,000 but the 99th percentile is
+ * around 115,000) this estimate may be very wrong.
+ *
+ * The memory usage for a typical histogram is roughly 3k * (# of buckets).  All
+ * insertion operations are amortized O(1), and all queries are O(# of buckets).
+ */
+template <class T, class TT=std::chrono::seconds,
+          class C=folly::MultiLevelTimeSeries<T, TT>>
+class TimeseriesHistogram {
+ private:
+   // NOTE: T must be equivalent to _signed_ numeric type for our math.
+   BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_signed);
+
+ public:
+  // values to be inserted into container
+  typedef T ValueType;
+  // the container type we use internally for each bucket
+  typedef C ContainerType;
+  // The time type.
+  typedef TT TimeType;
+
+  /*
+   * Create a TimeSeries histogram and initialize the bucketing and levels.
+   *
+   * The buckets are created by chopping the range [min, max) into pieces
+   * of size bucketSize, with the last bucket being potentially shorter.  Two
+   * additional buckets are always created -- the "under" bucket for the range
+   * (-inf, min) and the "over" bucket for the range [max, +inf).
+   *
+   * @param bucketSize the width of each bucket
+   * @param min the smallest value for the bucket range.
+   * @param max the largest value for the bucket range
+   * @param defaultContainer a pre-initialized timeseries with the desired
+   *                         number of levels and their durations.
+   */
+  TimeseriesHistogram(ValueType bucketSize, ValueType min, ValueType max,
+                      const ContainerType& defaultContainer);
+
+  /* Return the bucket size of each bucket in the histogram. */
+  ValueType getBucketSize() const { return buckets_.getBucketSize(); }
+
+  /* Return the min value at which bucketing begins. */
+  ValueType getMin() const { return buckets_.getMin(); }
+
+  /* Return the max value at which bucketing ends. */
+  ValueType getMax() const { return buckets_.getMax(); }
+
+  /* Return the number of levels of the Timeseries object in each bucket */
+  int getNumLevels() const {
+    return buckets_.getByIndex(0).numLevels();
+  }
+
+  /* Return the number of buckets */
+  int getNumBuckets() const { return buckets_.getNumBuckets(); }
+
+  /* Return the bucket index into which the given value would fall. */
+  int getBucketIdx(const ValueType& value) const;
+
+  /*
+   * Return the threshold of the bucket for the given index in range
+   * [0..numBuckets).  The bucket will have range [thresh, thresh + bucketSize)
+   * or [thresh, max), whichever is shorter.
+   */
+  ValueType getBucketMin(int bucketIdx) const {
+    return buckets_.getBucketMin(bucketIdx);
+  }
+
+  /* Return the actual timeseries in the given bucket (for reading only!) */
+  const ContainerType& getBucket(int bucketIdx) const {
+    return buckets_.getByIndex(bucketIdx);
+  }
+
+  /* Total count of values at the given timeseries level (all buckets). */
+  int64_t count(int level) const {
+    int64_t total = 0;
+    for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+      total += buckets_.getByIndex(b).count(level);
+    }
+    return total;
+  }
+
+  /* Total count of values added during the given interval (all buckets). */
+  int64_t count(TimeType start, TimeType end) const {
+    int64_t total = 0;
+    for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+      total += buckets_.getByIndex(b).count(start, end);
+    }
+    return total;
+  }
+
+  /* Total sum of values at the given timeseries level (all buckets). */
+  ValueType sum(int level) const {
+    ValueType total = ValueType();
+    for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+      total += buckets_.getByIndex(b).sum(level);
+    }
+    return total;
+  }
+
+  /* Total sum of values added during the given interval (all buckets). */
+  ValueType sum(TimeType start, TimeType end) const {
+    ValueType total = ValueType();
+    for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) {
+      total += buckets_.getByIndex(b).sum(start, end);
+    }
+    return total;
+  }
+
+  /* Average of values at the given timeseries level (all buckets). */
+  template <typename ReturnType=double>
+  ReturnType avg(int level) const;
+
+  /* Average of values added during the given interval (all buckets). */
+  template <typename ReturnType=double>
+  ReturnType avg(TimeType start, TimeType end) const;
+
+  /*
+   * Rate at the given timeseries level (all buckets).
+   * This is the sum of all values divided by the time interval (in seconds).
+   */
+  ValueType rate(int level) const;
+
+  /*
+   * Rate for the given interval (all buckets).
+   * This is the sum of all values divided by the time interval (in seconds).
+   */
+  template <typename ReturnType=double>
+  ReturnType rate(TimeType start, TimeType end) const;
+
+  /*
+   * Update every underlying timeseries object with the given timestamp. You
+   * must call this directly before querying to ensure that the data in all
+   * buckets is decayed properly.
+   */
+  void update(TimeType now);
+
+  /* clear all the data from the histogram. */
+  void clear();
+
+  /* Add a value into the histogram with timestamp 'now' */
+  void addValue(TimeType now, const ValueType& value);
+  /* Add a value the given number of times with timestamp 'now' */
+  void addValue(TimeType now, const ValueType& value, int64_t times);
+
+  /*
+   * Add all of the values from the specified histogram.
+   *
+   * All of the values will be added to the current time-slot.
+   *
+   * One use of this is for thread-local caching of frequently updated
+   * histogram data.  For example, each thread can store a thread-local
+   * Histogram that is updated frequently, and only add it to the global
+   * TimeseriesHistogram once a second.
+   */
+  void addValues(TimeType now, const folly::Histogram<ValueType>& values);
+
+  /*
+   * Return an estimate of the value at the given percentile in the histogram
+   * in the given timeseries level.  The percentile is estimated as follows:
+   *
+   * - We retrieve a count of the values in each bucket (at the given level)
+   * - We determine via the counts which bucket the given percentile falls in.
+   * - We assume the average value in the bucket is also its median
+   * - We then linearly interpolate within the bucket, by assuming that the
+   *   distribution is uniform in the two value ranges [left, median) and
+   *   [median, right) where [left, right) is the bucket value range.
+   *
+   * Caveats:
+   * - If the histogram is empty, this always returns ValueType(), usually 0.
+   * - For the 'under' and 'over' special buckets, their range is unbounded
+   *   on one side.  In order for the interpolation to work, we assume that
+   *   the average value in the bucket is equidistant from the two edges of
+   *   the bucket.  In other words, we assume that the distance between the
+   *   average and the known bound is equal to the distance between the average
+   *   and the unknown bound.
+   */
+  ValueType getPercentileEstimate(double pct, int level) const;
+  /*
+   * Return an estimate of the value at the given percentile in the histogram
+   * in the given historical interval.  Please see the documentation for
+   * getPercentileEstimate(int pct, int level) for the explanation of the
+   * estimation algorithm.
+   */
+  ValueType getPercentileEstimate(double pct, TimeType start, TimeType end)
+    const;
+
+  /*
+   * Return the bucket index that the given percentile falls into (in the
+   * given timeseries level).  This index can then be used to retrieve either
+   * the bucket threshold, or other data from inside the bucket.
+   */
+  int getPercentileBucketIdx(double pct, int level) const;
+  /*
+   * Return the bucket index that the given percentile falls into (in the
+   * given historical interval).  This index can then be used to retrieve either
+   * the bucket threshold, or other data from inside the bucket.
+   */
+  int getPercentileBucketIdx(double pct, TimeType start, TimeType end) const;
+
+  /* Get the bucket threshold for the bucket containing the given pct. */
+  int getPercentileBucketMin(double pct, int level) const {
+    return getBucketMin(getPercentileBucketIdx(pct, level));
+  }
+  /* Get the bucket threshold for the bucket containing the given pct. */
+  int getPercentileBucketMin(double pct, TimeType start, TimeType end) const {
+    return getBucketMin(getPercentileBucketIdx(pct, start, end));
+  }
+
+  /*
+   * Print out serialized data from all buckets at the given level.
+   * Format is: BUCKET [',' BUCKET ...]
+   * Where: BUCKET == bucketMin ':' count ':' avg
+   */
+  std::string getString(int level) const;
+
+  /*
+   * Print out serialized data for all buckets in the historical interval.
+   * For format, please see getString(int level).
+   */
+  std::string getString(TimeType start, TimeType end) const;
+
+ private:
+  typedef ContainerType Bucket;
+  struct CountFromLevel {
+    explicit CountFromLevel(int level) : level_(level) {}
+
+    uint64_t operator()(const ContainerType& bucket) const {
+      return bucket.count(level_);
+    }
+
+   private:
+    int level_;
+  };
+  struct CountFromInterval {
+    explicit CountFromInterval(TimeType start, TimeType end)
+      : start_(start),
+        end_(end) {}
+
+    uint64_t operator()(const ContainerType& bucket) const {
+      return bucket.count(start_, end_);
+    }
+
+   private:
+    TimeType start_;
+    TimeType end_;
+  };
+
+  struct AvgFromLevel {
+    explicit AvgFromLevel(int level) : level_(level) {}
+
+    ValueType operator()(const ContainerType& bucket) const {
+      return bucket.template avg<ValueType>(level_);
+    }
+
+   private:
+    int level_;
+  };
+
+  template <typename ReturnType>
+  struct AvgFromInterval {
+    explicit AvgFromInterval(TimeType start, TimeType end)
+      : start_(start),
+        end_(end) {}
+
+    ReturnType operator()(const ContainerType& bucket) const {
+      return bucket.template avg<ReturnType>(start_, end_);
+    }
+
+   private:
+    TimeType start_;
+    TimeType end_;
+  };
+
+  /*
+   * Special logic for the case of only one unique value registered
+   * (this can happen when clients don't pick good bucket ranges or have
+   * other bugs).  It's a lot easier for clients to track down these issues
+   * if they are getting the correct value.
+   */
+  void maybeHandleSingleUniqueValue(const ValueType& value);
+
+  folly::detail::HistogramBuckets<ValueType, ContainerType> buckets_;
+  bool haveNotSeenValue_;
+  bool singleUniqueValue_;
+  ValueType firstValue_;
+};
+
+}  // folly
+
+#endif  // FOLLY_TIMESERIES_HISTOGRAM_H_
diff --git a/faux-folly/folly/test/ApplyTupleTest.cpp b/faux-folly/folly/test/ApplyTupleTest.cpp
new file mode 100644
index 0000000..971f5b6
--- /dev/null
+++ b/faux-folly/folly/test/ApplyTupleTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2015 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 <iostream>
+
+#include <folly/ApplyTuple.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+// this placates visual studio stupidity - see
+// http://stackoverflow.com/questions/5503901
+namespace {}
+
+namespace {
+
+void func(int a, int b, double c) {
+  EXPECT_EQ(a, 1);
+  EXPECT_EQ(b, 2);
+  EXPECT_EQ(c, 3.0);
+}
+
+struct Wat {
+  void func(int a, int b, double c) {
+    ::func(a, b, c);
+  }
+
+  double retVal(int a, double b) {
+    return a + b;
+  }
+
+  Wat() {}
+  Wat(Wat const&) = delete;
+
+  int foo;
+};
+
+struct Overloaded {
+  int func(int) { return 0; }
+  bool func(bool) { return true; }
+};
+
+struct Func {
+  int operator()() const {
+    return 1;
+  }
+};
+
+struct CopyCount {
+  CopyCount() {}
+  CopyCount(CopyCount const&) {
+    std::cout << "copy count copy ctor\n";
+  }
+};
+
+void anotherFunc(CopyCount const&) {}
+
+std::function<void (int, int, double)> makeFunc() {
+  return &func;
+}
+
+struct GuardObjBase {
+  GuardObjBase(GuardObjBase&&) noexcept {}
+  GuardObjBase() {}
+  GuardObjBase(GuardObjBase const&) = delete;
+  GuardObjBase& operator=(GuardObjBase const&) = delete;
+};
+typedef GuardObjBase const& Guard;
+
+template<class F, class Tuple>
+struct GuardObj : GuardObjBase {
+  explicit GuardObj(F&& f, Tuple&& args)
+    : f_(std::move(f))
+    , args_(std::move(args))
+  {}
+  GuardObj(GuardObj&& g) noexcept
+    : GuardObjBase(std::move(g))
+    , f_(std::move(g.f_))
+    , args_(std::move(g.args_))
+  {}
+
+  ~GuardObj() {
+    folly::applyTuple(f_, args_);
+  }
+
+  GuardObj(const GuardObj&) = delete;
+  GuardObj& operator=(const GuardObj&) = delete;
+
+private:
+  F f_;
+  Tuple args_;
+};
+
+template<class F, class ...Args>
+GuardObj<typename std::decay<F>::type,std::tuple<Args...>>
+guard(F&& f, Args&&... args) {
+  return GuardObj<typename std::decay<F>::type,std::tuple<Args...>>(
+    std::forward<F>(f),
+    std::tuple<Args...>(std::forward<Args>(args)...)
+  );
+}
+
+struct Mover {
+  Mover() {}
+  Mover(Mover&&) noexcept {}
+  Mover(const Mover&) = delete;
+  Mover& operator=(const Mover&) = delete;
+};
+
+void move_only_func(Mover&&) {}
+
+}
+
+TEST(ApplyTuple, Test) {
+  auto argsTuple = std::make_tuple(1, 2, 3.0);
+  auto func2 = func;
+  folly::applyTuple(func2, argsTuple);
+  folly::applyTuple(func, argsTuple);
+  folly::applyTuple(func, std::make_tuple(1, 2, 3.0));
+  folly::applyTuple(makeFunc(), std::make_tuple(1, 2, 3.0));
+  folly::applyTuple(makeFunc(), argsTuple);
+
+  std::unique_ptr<Wat> wat(new Wat);
+  folly::applyTuple(&Wat::func, std::make_tuple(wat.get(), 1, 2, 3.0));
+  auto argsTuple2 = std::make_tuple(wat.get(), 1, 2, 3.0);
+  folly::applyTuple(&Wat::func, argsTuple2);
+
+  EXPECT_EQ(10.0,
+            folly::applyTuple(&Wat::retVal,
+                              std::make_tuple(wat.get(), 1, 9.0)));
+
+  auto test = guard(func, 1, 2, 3.0);
+  CopyCount cpy;
+  auto test2 = guard(anotherFunc, cpy);
+  auto test3 = guard(anotherFunc, std::cref(cpy));
+
+  Overloaded ovl;
+  EXPECT_EQ(0,
+            folly::applyTuple(
+              static_cast<int (Overloaded::*)(int)>(&Overloaded::func),
+              std::make_tuple(&ovl, 12)));
+  EXPECT_EQ(true,
+            folly::applyTuple(
+              static_cast<bool (Overloaded::*)(bool)>(&Overloaded::func),
+              std::make_tuple(&ovl, false)));
+
+  int x = folly::applyTuple(std::plus<int>(), std::make_tuple(12, 12));
+  EXPECT_EQ(24, x);
+
+  Mover m;
+  folly::applyTuple(move_only_func,
+                    std::forward_as_tuple(std::forward<Mover>(Mover())));
+  const auto tuple3 = std::make_tuple(1, 2, 3.0);
+  folly::applyTuple(func, tuple3);
+}
diff --git a/faux-folly/folly/test/AsciiCaseInsensitiveBenchmark.cpp b/faux-folly/folly/test/AsciiCaseInsensitiveBenchmark.cpp
new file mode 100644
index 0000000..0086576
--- /dev/null
+++ b/faux-folly/folly/test/AsciiCaseInsensitiveBenchmark.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 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/Benchmark.h>
+#include <folly/Range.h>
+
+#include <algorithm>
+
+using namespace std;
+using namespace folly;
+
+string lorem_ipsum =
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam"
+"lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam"
+"viverra nec consectetur ante hendrerit. Donec et mollis"
+"dolor. Praesent et diam eget libero egestas mattis sit amet vitae"
+"augue. Nam tincidunt congue enim, ut porta lorem lacinia"
+"consectetur. Donec ut libero sed arcu vehicula ultricies a non"
+"tortor. Lorem ipsum dolor sit amet, consectetur adipiscing"
+"elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed,"
+"adipiscing id dolor. Pellentesque auctor nisi id magna consequat"
+"sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt"
+"feugiat nisl imperdiet. Ut convallis libero in urna ultrices"
+"accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at"
+"malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis"
+"parturient montes, nascetur ridiculus mus. In rutrum accumsan"
+"ultricies. Mauris vitae nisi at sem facilisis semper ac in est."
+"\n"
+"Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut"
+"tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper"
+"ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi"
+"diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec"
+"pretium volutpat, arcu ante placerat erat, non tristique elit urna et"
+"turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et"
+"orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam,"
+"elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis"
+"pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod"
+"ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo"
+"sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at"
+"massa ipsum. Quisque bibendum purus convallis nulla ultrices"
+"ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit"
+"laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat"
+"elit. Nam sagittis nisi dui."
+"\n"
+"Suspendisse lectus leo, consectetur in tempor sit amet, placerat quis"
+"neque. Etiam luctus porttitor lorem, sed suscipit est rutrum"
+"non. Curabitur lobortis nisl a enim congue semper. Aenean commodo"
+"ultrices imperdiet. Vestibulum ut justo vel sapien venenatis"
+"tincidunt. Phasellus eget dolor sit amet ipsum dapibus condimentum"
+"vitae quis lectus. Aliquam ut massa in turpis dapibus"
+"convallis. Praesent elit lacus, vestibulum at malesuada et, ornare et"
+"est. Ut augue nunc, sodales ut euismod non, adipiscing vitae"
+"orci. Mauris ut placerat justo. Mauris in ultricies enim. Quisque nec"
+"est eleifend nulla ultrices egestas quis ut quam. Donec sollicitudin"
+"lectus a mauris pulvinar id aliquam urna cursus. Cras quis ligula sem,"
+"vel elementum mi. Phasellus non ullamcorper urna."
+"\n"
+"Class aptent taciti sociosqu ad litora torquent per conubia nostra,"
+"per inceptos himenaeos. In euismod ultrices facilisis. Vestibulum"
+"porta sapien adipiscing augue congue id pretium lectus molestie. Proin"
+"quis dictum nisl. Morbi id quam sapien, sed vestibulum sem. Duis"
+"elementum rutrum mauris sed convallis. Proin vestibulum magna"
+"mi. Aenean tristique hendrerit magna, ac facilisis nulla hendrerit"
+"ut. Sed non tortor sodales quam auctor elementum. Donec hendrerit nunc"
+"eget elit pharetra pulvinar. Suspendisse id tempus tortor. Aenean"
+"luctus, elit commodo laoreet commodo, justo nisi consequat massa, sed"
+"vulputate quam urna quis eros. Donec vel."
+"\n";
+
+const string needle = "commodo";
+
+// legacy implementation
+struct AsciiCaseInsensitiveLegacy {
+  bool operator()(char lhs, char rhs) const {
+    return toupper(lhs) == toupper(rhs);
+  }
+};
+
+template<typename Cmp>
+inline void test_operator_on_search(int iters) {
+  Cmp cmp;
+  int dummy = 0;
+  for (int i = 0; i < iters; ++i) {
+    dummy += std::search(
+      lorem_ipsum.begin(), lorem_ipsum.end(),
+      needle.begin(), needle.end(),
+      cmp
+    ) - lorem_ipsum.begin();
+  }
+}
+
+BENCHMARK(LegacyCaseInsensitiveCheck, iters) {
+  test_operator_on_search<AsciiCaseInsensitiveLegacy>(iters);
+}
+
+BENCHMARK(CurrentCaseInsensitiveCheck, iters) {
+  test_operator_on_search<AsciiCaseInsensitive>(iters);
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return 0;
+}
+
+/*
+============================================================================
+folly/test/AsciiCaseInsensitiveBenchmark.cpp    relative  time/iter  iters/s
+============================================================================
+LegacyCaseInsensitiveCheck                                   9.04us  110.60K
+CurrentCaseInsensitiveCheck                                  2.96us  337.59K
+============================================================================
+*/
diff --git a/faux-folly/folly/test/AsciiCaseInsensitiveTest.cpp b/faux-folly/folly/test/AsciiCaseInsensitiveTest.cpp
new file mode 100644
index 0000000..1177118
--- /dev/null
+++ b/faux-folly/folly/test/AsciiCaseInsensitiveTest.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 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/Range.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+
+using namespace std;
+using namespace folly;
+
+TEST(CaseInsensitiveMatch, CompareWithLegacy) {
+  AsciiCaseInsensitive cmp;
+  for (int i=0; i<(1<<8); i++) {
+    EXPECT_TRUE(cmp(tolower(i), toupper(i)));
+    EXPECT_TRUE(cmp(toupper(i), tolower(i)));
+  }
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/AtomicHashArrayTest.cpp b/faux-folly/folly/test/AtomicHashArrayTest.cpp
new file mode 100644
index 0000000..c1eaf6d
--- /dev/null
+++ b/faux-folly/folly/test/AtomicHashArrayTest.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2015 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 <sys/mman.h>
+
+#include <cstddef>
+#include <map>
+#include <stdexcept>
+
+#include <folly/AtomicHashArray.h>
+#include <folly/Hash.h>
+#include <folly/Conv.h>
+#include <folly/Memory.h>
+#include <gtest/gtest.h>
+
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+using namespace std;
+using namespace folly;
+
+template <class T>
+class MmapAllocator {
+ public:
+  typedef T value_type;
+  typedef T* pointer;
+  typedef const T* const_pointer;
+  typedef T& reference;
+  typedef const T& const_reference;
+
+  typedef ptrdiff_t difference_type;
+  typedef size_t size_type;
+
+  T* address(T& x) const {
+    return std::addressof(x);
+  }
+
+  const T* address(const T& x) const {
+    return std::addressof(x);
+  }
+
+  size_t max_size() const {
+    return std::numeric_limits<size_t>::max();
+  }
+
+  template <class U> struct rebind {
+    typedef MmapAllocator<U> other;
+  };
+
+  bool operator!=(const MmapAllocator<T>& other) const {
+    return !(*this == other);
+  }
+
+  bool operator==(const MmapAllocator<T>& other) const {
+    return true;
+  }
+
+  template <class... Args>
+  void construct(T* p, Args&&... args) {
+    new (p) T(std::forward<Args>(args)...);
+  }
+
+  void destroy(T* p) {
+    p->~T();
+  }
+
+  T *allocate(size_t n) {
+    void *p = mmap(nullptr, n * sizeof(T), PROT_READ | PROT_WRITE,
+        MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+    if (p == MAP_FAILED) throw std::bad_alloc();
+    return (T *)p;
+  }
+
+  void deallocate(T *p, size_t n) {
+    munmap(p, n * sizeof(T));
+  }
+};
+
+template<class KeyT, class ValueT>
+pair<KeyT,ValueT> createEntry(int i) {
+  return pair<KeyT,ValueT>(to<KeyT>(folly::hash::jenkins_rev_mix32(i) % 1000),
+                           to<ValueT>(i + 3));
+}
+
+template<class KeyT, class ValueT, class Allocator = std::allocator<char>>
+void testMap() {
+  typedef AtomicHashArray<KeyT, ValueT, std::hash<KeyT>,
+                          std::equal_to<KeyT>, Allocator> MyArr;
+  auto arr = MyArr::create(150);
+  map<KeyT, ValueT> ref;
+  for (int i = 0; i < 100; ++i) {
+    auto e = createEntry<KeyT, ValueT>(i);
+    auto ret = arr->insert(e);
+    EXPECT_EQ(!ref.count(e.first), ret.second);  // succeed iff not in ref
+    ref.insert(e);
+    EXPECT_EQ(ref.size(), arr->size());
+    if (ret.first == arr->end()) {
+      EXPECT_FALSE("AHA should not have run out of space.");
+      continue;
+    }
+    EXPECT_EQ(e.first, ret.first->first);
+    EXPECT_EQ(ref.find(e.first)->second, ret.first->second);
+  }
+
+  for (int i = 125; i > 0; i -= 10) {
+    auto e = createEntry<KeyT, ValueT>(i);
+    auto ret = arr->erase(e.first);
+    auto refRet = ref.erase(e.first);
+    EXPECT_EQ(ref.size(), arr->size());
+    EXPECT_EQ(refRet, ret);
+  }
+
+  for (int i = 155; i > 0; i -= 10) {
+    auto e = createEntry<KeyT, ValueT>(i);
+    auto ret = arr->insert(e);
+    auto refRet = ref.insert(e);
+    EXPECT_EQ(ref.size(), arr->size());
+    EXPECT_EQ(*refRet.first, *ret.first);
+    EXPECT_EQ(refRet.second, ret.second);
+  }
+
+  for (const auto& e : ref) {
+    auto ret = arr->find(e.first);
+    if (ret == arr->end()) {
+      EXPECT_FALSE("Key was not in AHA");
+      continue;
+    }
+    EXPECT_EQ(e.first, ret->first);
+    EXPECT_EQ(e.second, ret->second);
+  }
+}
+
+template<class KeyT, class ValueT, class Allocator = std::allocator<char>>
+void testNoncopyableMap() {
+  typedef AtomicHashArray<KeyT, std::unique_ptr<ValueT>, std::hash<KeyT>,
+                          std::equal_to<KeyT>, Allocator> MyArr;
+  auto arr = MyArr::create(150);
+  for (int i = 0; i < 100; i++) {
+    arr->insert(make_pair(i,std::unique_ptr<ValueT>(new ValueT(i))));
+  }
+  for (int i = 0; i < 100; i++) {
+    auto ret = arr->find(i);
+    EXPECT_EQ(*(ret->second), i);
+  }
+}
+
+
+TEST(Aha, InsertErase_i32_i32) {
+  testMap<int32_t, int32_t>();
+  testMap<int32_t, int32_t, MmapAllocator<char>>();
+  testNoncopyableMap<int32_t, int32_t>();
+  testNoncopyableMap<int32_t, int32_t, MmapAllocator<char>>();
+}
+TEST(Aha, InsertErase_i64_i32) {
+  testMap<int64_t, int32_t>();
+  testMap<int64_t, int32_t, MmapAllocator<char>>();
+  testNoncopyableMap<int64_t, int32_t>();
+  testNoncopyableMap<int64_t, int32_t, MmapAllocator<char>>();
+}
+TEST(Aha, InsertErase_i64_i64) {
+  testMap<int64_t, int64_t>();
+  testMap<int64_t, int64_t, MmapAllocator<char>>();
+  testNoncopyableMap<int64_t, int64_t>();
+  testNoncopyableMap<int64_t, int64_t, MmapAllocator<char>>();
+}
+TEST(Aha, InsertErase_i32_i64) {
+  testMap<int32_t, int64_t>();
+  testMap<int32_t, int64_t, MmapAllocator<char>>();
+  testNoncopyableMap<int32_t, int64_t>();
+  testNoncopyableMap<int32_t, int64_t, MmapAllocator<char>>();
+}
+TEST(Aha, InsertErase_i32_str) {
+  testMap<int32_t, string>();
+  testMap<int32_t, string, MmapAllocator<char>>();
+}
+TEST(Aha, InsertErase_i64_str) {
+  testMap<int64_t, string>();
+  testMap<int64_t, string, MmapAllocator<char>>();
+}
+
+TEST(Aha, Create_cstr_i64) {
+  auto obj = AtomicHashArray<const char*, int64_t>::create(12);
+}
diff --git a/faux-folly/folly/test/AtomicStructTest.cpp b/faux-folly/folly/test/AtomicStructTest.cpp
new file mode 100644
index 0000000..396cf37
--- /dev/null
+++ b/faux-folly/folly/test/AtomicStructTest.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 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/AtomicStruct.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+struct TwoBy32 {
+  uint32_t left;
+  uint32_t right;
+};
+
+TEST(AtomicStruct, two_by_32) {
+  AtomicStruct<TwoBy32> a(TwoBy32{ 10, 20 });
+  TwoBy32 av = a;
+  EXPECT_EQ(av.left, 10);
+  EXPECT_EQ(av.right, 20);
+  EXPECT_TRUE(a.compare_exchange_strong(av, TwoBy32{ 30, 40 }));
+  EXPECT_FALSE(a.compare_exchange_weak(av, TwoBy32{ 31, 41 }));
+  EXPECT_EQ(av.left, 30);
+  EXPECT_TRUE(a.is_lock_free());
+  auto b = a.exchange(TwoBy32{ 50, 60 });
+  EXPECT_EQ(b.left, 30);
+  EXPECT_EQ(b.right, 40);
+  EXPECT_EQ(a.load().left, 50);
+  a = TwoBy32{ 70, 80 };
+  EXPECT_EQ(a.load().right, 80);
+  a.store(TwoBy32{ 90, 100 });
+  av = a;
+  EXPECT_EQ(av.left, 90);
+  AtomicStruct<TwoBy32> c;
+  c = b;
+  EXPECT_EQ(c.load().right, 40);
+}
+
+TEST(AtomicStruct, size_selection) {
+  struct S1 { char x[1]; };
+  struct S2 { char x[2]; };
+  struct S3 { char x[3]; };
+  struct S4 { char x[4]; };
+  struct S5 { char x[5]; };
+  struct S6 { char x[6]; };
+  struct S7 { char x[7]; };
+  struct S8 { char x[8]; };
+
+  EXPECT_EQ(sizeof(AtomicStruct<S1>), 1);
+  EXPECT_EQ(sizeof(AtomicStruct<S2>), 2);
+  EXPECT_EQ(sizeof(AtomicStruct<S3>), 4);
+  EXPECT_EQ(sizeof(AtomicStruct<S4>), 4);
+  EXPECT_EQ(sizeof(AtomicStruct<S5>), 8);
+  EXPECT_EQ(sizeof(AtomicStruct<S6>), 8);
+  EXPECT_EQ(sizeof(AtomicStruct<S7>), 8);
+  EXPECT_EQ(sizeof(AtomicStruct<S8>), 8);
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/BatonTest.cpp b/faux-folly/folly/test/BatonTest.cpp
new file mode 100644
index 0000000..b970c1a
--- /dev/null
+++ b/faux-folly/folly/test/BatonTest.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2015 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/Baton.h>
+#include <folly/test/DeterministicSchedule.h>
+#include <thread>
+#include <semaphore.h>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include <folly/Benchmark.h>
+
+using namespace folly;
+using namespace folly::test;
+using folly::detail::EmulatedFutexAtomic;
+
+typedef DeterministicSchedule DSched;
+
+TEST(Baton, basic) {
+  Baton<> b;
+  b.post();
+  b.wait();
+}
+
+template <template<typename> class Atom>
+void run_pingpong_test(int numRounds) {
+  Baton<Atom> batons[17];
+  Baton<Atom>& a = batons[0];
+  Baton<Atom>& b = batons[16]; // to get it on a different cache line
+  auto thr = DSched::thread([&]{
+    for (int i = 0; i < numRounds; ++i) {
+      a.wait();
+      a.reset();
+      b.post();
+    }
+  });
+  for (int i = 0; i < numRounds; ++i) {
+    a.post();
+    b.wait();
+    b.reset();
+  }
+  DSched::join(thr);
+}
+
+TEST(Baton, pingpong) {
+  DSched sched(DSched::uniform(0));
+
+  run_pingpong_test<DeterministicAtomic>(1000);
+}
+
+BENCHMARK(baton_pingpong, iters) {
+  run_pingpong_test<std::atomic>(iters);
+}
+
+BENCHMARK(baton_pingpong_emulated_futex, iters) {
+  run_pingpong_test<EmulatedFutexAtomic>(iters);
+}
+
+BENCHMARK(posix_sem_pingpong, iters) {
+  sem_t sems[3];
+  sem_t* a = sems + 0;
+  sem_t* b = sems + 2; // to get it on a different cache line
+
+  sem_init(a, 0, 0);
+  sem_init(b, 0, 0);
+  auto thr = std::thread([=]{
+    for (size_t i = 0; i < iters; ++i) {
+      sem_wait(a);
+      sem_post(b);
+    }
+  });
+  for (size_t i = 0; i < iters; ++i) {
+    sem_post(a);
+    sem_wait(b);
+  }
+  thr.join();
+}
+
+template <template<typename> class Atom, typename Clock>
+void run_basic_timed_wait_tests() {
+  Baton<Atom> b;
+  b.post();
+  // tests if early delivery works fine
+  EXPECT_TRUE(b.timed_wait(Clock::now()));
+}
+
+template <template<typename> class Atom, typename Clock>
+void run_timed_wait_tmo_tests() {
+  Baton<Atom> b;
+
+  auto thr = DSched::thread([&]{
+    bool rv = b.timed_wait(Clock::now() + std::chrono::milliseconds(1));
+    // main thread is guaranteed to not post until timeout occurs
+    EXPECT_FALSE(rv);
+  });
+  DSched::join(thr);
+}
+
+template <template<typename> class Atom, typename Clock>
+void run_timed_wait_regular_test() {
+  Baton<Atom> b;
+
+  auto thr = DSched::thread([&] {
+    // To wait forever we'd like to use time_point<Clock>::max, but
+    // std::condition_variable does math to convert the timeout to
+    // system_clock without handling overflow.
+    auto farFuture = Clock::now() + std::chrono::hours(1000);
+    bool rv = b.timed_wait(farFuture);
+    if (!std::is_same<Atom<int>, DeterministicAtomic<int>>::value) {
+      // DeterministicAtomic ignores actual times, so doesn't guarantee
+      // a lack of timeout
+      EXPECT_TRUE(rv);
+    }
+  });
+
+  if (!std::is_same<Atom<int>, DeterministicAtomic<int>>::value) {
+    // If we are using std::atomic (or EmulatedFutexAtomic) then
+    // a sleep here guarantees to a large extent that 'thr' will
+    // execute wait before we post it, thus testing late delivery. For
+    // DeterministicAtomic, we just rely on DeterministicSchedule to do
+    // the scheduling.  The test won't fail if we lose the race, we just
+    // don't get coverage.
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+  }
+
+  b.post();
+  DSched::join(thr);
+}
+
+TEST(Baton, timed_wait_basic_system_clock) {
+  run_basic_timed_wait_tests<std::atomic, std::chrono::system_clock>();
+  run_basic_timed_wait_tests<EmulatedFutexAtomic, std::chrono::system_clock>();
+  run_basic_timed_wait_tests<DeterministicAtomic, std::chrono::system_clock>();
+}
+
+TEST(Baton, timed_wait_timeout_system_clock) {
+  run_timed_wait_tmo_tests<std::atomic, std::chrono::system_clock>();
+  run_timed_wait_tmo_tests<EmulatedFutexAtomic, std::chrono::system_clock>();
+  run_timed_wait_tmo_tests<DeterministicAtomic, std::chrono::system_clock>();
+}
+
+TEST(Baton, timed_wait_system_clock) {
+  run_timed_wait_regular_test<std::atomic, std::chrono::system_clock>();
+  run_timed_wait_regular_test<EmulatedFutexAtomic, std::chrono::system_clock>();
+  run_timed_wait_regular_test<DeterministicAtomic, std::chrono::system_clock>();
+}
+
+TEST(Baton, timed_wait_basic_steady_clock) {
+  run_basic_timed_wait_tests<std::atomic, std::chrono::steady_clock>();
+  run_basic_timed_wait_tests<EmulatedFutexAtomic, std::chrono::steady_clock>();
+  run_basic_timed_wait_tests<DeterministicAtomic, std::chrono::steady_clock>();
+}
+
+TEST(Baton, timed_wait_timeout_steady_clock) {
+  run_timed_wait_tmo_tests<std::atomic, std::chrono::steady_clock>();
+  run_timed_wait_tmo_tests<EmulatedFutexAtomic, std::chrono::steady_clock>();
+  run_timed_wait_tmo_tests<DeterministicAtomic, std::chrono::steady_clock>();
+}
+
+TEST(Baton, timed_wait_steady_clock) {
+  run_timed_wait_regular_test<std::atomic, std::chrono::steady_clock>();
+  run_timed_wait_regular_test<EmulatedFutexAtomic, std::chrono::steady_clock>();
+  run_timed_wait_regular_test<DeterministicAtomic, std::chrono::steady_clock>();
+}
+
+template <template<typename> class Atom>
+void run_try_wait_tests() {
+  Baton<Atom> b;
+  EXPECT_FALSE(b.try_wait());
+  b.post();
+  EXPECT_TRUE(b.try_wait());
+}
+
+TEST(Baton, try_wait) {
+  run_try_wait_tests<std::atomic>();
+  run_try_wait_tests<EmulatedFutexAtomic>();
+  run_try_wait_tests<DeterministicAtomic>();
+}
+
+// I am omitting a benchmark result snapshot because these microbenchmarks
+// mainly illustrate that PreBlockAttempts is very effective for rapid
+// handoffs.  The performance of Baton and sem_t is essentially identical
+// to the required futex calls for the blocking case
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto rv = RUN_ALL_TESTS();
+  if (!rv && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return rv;
+}
diff --git a/faux-folly/folly/test/BenchmarkTest.cpp b/faux-folly/folly/test/BenchmarkTest.cpp
new file mode 100644
index 0000000..116ecb3
--- /dev/null
+++ b/faux-folly/folly/test/BenchmarkTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2015 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/Benchmark.h>
+#include <folly/Foreach.h>
+#include <folly/String.h>
+#include <algorithm>
+#include <iostream>
+#include <numeric>
+#include <random>
+#include <vector>
+using namespace folly;
+using namespace std;
+
+void fun() {
+  static double x = 1;
+  ++x;
+  doNotOptimizeAway(x);
+}
+BENCHMARK(bmFun) { fun(); }
+BENCHMARK(bmRepeatedFun, n) {
+  FOR_EACH_RANGE (i, 0, n) {
+    fun();
+  }
+}
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(gun) {
+  static double x = 1;
+  x *= 2000;
+  doNotOptimizeAway(x);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(baselinevector) {
+  vector<int> v;
+
+  BENCHMARK_SUSPEND {
+    v.resize(1000);
+  }
+
+  FOR_EACH_RANGE (i, 0, 100) {
+    v.push_back(42);
+  }
+}
+
+BENCHMARK_RELATIVE(bmVector) {
+  vector<int> v;
+  FOR_EACH_RANGE (i, 0, 100) {
+    v.resize(v.size() + 1, 42);
+  }
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(superslow) {
+  sleep(1);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(noMulti) {
+  fun();
+}
+
+BENCHMARK_MULTI(multiSimple) {
+  FOR_EACH_RANGE (i, 0, 10) {
+    fun();
+  }
+  return 10;
+}
+
+BENCHMARK_RELATIVE_MULTI(multiSimpleRel) {
+  FOR_EACH_RANGE (i, 0, 10) {
+    fun();
+    fun();
+  }
+  return 10;
+}
+
+BENCHMARK_MULTI(multiIterArgs, iter) {
+  FOR_EACH_RANGE (i, 0, 10 * iter) {
+    fun();
+  }
+  return 10 * iter;
+}
+
+BENCHMARK_RELATIVE_MULTI(multiIterArgsRel, iter) {
+  FOR_EACH_RANGE (i, 0, 10 * iter) {
+    fun();
+    fun();
+  }
+  return 10 * iter;
+}
+
+unsigned paramMulti(unsigned iter, unsigned num) {
+  for (unsigned i = 0; i < iter; ++i) {
+    for (unsigned j = 0; j < num; ++j) {
+      fun();
+    }
+  }
+  return num * iter;
+}
+
+unsigned paramMultiRel(unsigned iter, unsigned num) {
+  for (unsigned i = 0; i < iter; ++i) {
+    for (unsigned j = 0; j < num; ++j) {
+      fun();
+      fun();
+    }
+  }
+  return num * iter;
+}
+
+BENCHMARK_PARAM_MULTI(paramMulti, 1);
+BENCHMARK_RELATIVE_PARAM_MULTI(paramMultiRel, 1);
+
+BENCHMARK_PARAM_MULTI(paramMulti, 5);
+BENCHMARK_RELATIVE_PARAM_MULTI(paramMultiRel, 5);
+
+BENCHMARK_DRAW_LINE();
+
+BENCHMARK(BenchmarkSuspender_dismissing_void, iter) {
+  BenchmarkSuspender braces;
+  mt19937_64 rng;
+  while (iter--) {
+    vector<size_t> v(1 << 12, 0);
+    iota(v.begin(), v.end(), 0);
+    shuffle(v.begin(), v.end(), rng);
+    braces.dismissing([&] {
+        sort(v.begin(), v.end());
+    });
+  }
+}
+
+BENCHMARK(BenchmarkSuspender_dismissing_value, iter) {
+  BenchmarkSuspender braces;
+  mt19937_64 rng;
+  while (iter--) {
+    vector<size_t> v(1 << 12, 0);
+    iota(v.begin(), v.end(), 0);
+    shuffle(v.begin(), v.end(), rng);
+    auto s = braces.dismissing([&] {
+        sort(v.begin(), v.end());
+        return accumulate(v.begin(), v.end(), 0, [](size_t a, size_t e) {
+            return a + e;
+        });
+    });
+    doNotOptimizeAway(s);
+  }
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  runBenchmarks();
+  runBenchmarksOnFlag();
+}
diff --git a/faux-folly/folly/test/BitIteratorTest.cpp b/faux-folly/folly/test/BitIteratorTest.cpp
new file mode 100644
index 0000000..e5119b9
--- /dev/null
+++ b/faux-folly/folly/test/BitIteratorTest.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2015 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/Bits.h>
+#include <folly/Benchmark.h>
+
+#include <algorithm>
+#include <type_traits>
+#include <limits>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include <gflags/gflags.h>
+
+using namespace folly;
+using namespace folly::bititerator_detail;
+
+namespace {
+
+template <class INT, class IT>
+void checkIt(INT exp, IT& it) {
+  typedef typename std::make_unsigned<INT>::type utype;
+  size_t bits = std::numeric_limits<utype>::digits;
+  utype uexp = exp;
+  for (size_t i = 0; i < bits; ++i) {
+    bool e = uexp & 1;
+    EXPECT_EQ(e, *it++);
+    uexp >>= 1;
+  }
+}
+
+template <class INT, class IT>
+void checkRange(INT exp, IT begin, IT end) {
+  typedef typename std::make_unsigned<INT>::type utype;
+  utype uexp = exp;
+  size_t i = 0;
+  auto bitEnd = makeBitIterator(end);
+  for (BitIterator<IT> it = makeBitIterator(begin); it != bitEnd; ++it, ++i) {
+    bool e = uexp & 1;
+    EXPECT_EQ(e, *it);
+    uexp >>= 1;
+  }
+}
+
+}  // namespace
+
+TEST(BitIterator, Simple) {
+  std::vector<int> v;
+  v.push_back(0x10);
+  v.push_back(0x42);
+  auto bi(makeBitIterator(v.begin()));
+  checkIt(0x10, bi);
+  checkIt(0x42, bi);
+  checkRange(0x0000004200000010ULL, v.begin(), v.end());
+
+  v[0] = 0;
+  bi = v.begin();
+  *bi++ = true;     // 1
+  *bi++ = false;
+  *bi++ = true;     // 4
+  *bi++ = false;
+  *bi++ = false;
+  *bi++ = true;     // 32
+  *++bi = true;     // 128 (note pre-increment)
+
+  EXPECT_EQ(165, v[0]);
+}
+
+TEST(BitIterator, Const) {
+  std::vector<int> v;
+  v.push_back(0x10);
+  v.push_back(0x42);
+  auto bi(makeBitIterator(v.cbegin()));
+  checkIt(0x10, bi);
+  checkIt(0x42, bi);
+}
+
+namespace {
+
+template <class BaseIter>
+BitIterator<BaseIter> simpleFFS(BitIterator<BaseIter> begin,
+                                BitIterator<BaseIter> end) {
+  return std::find(begin, end, true);
+}
+
+template <class FFS>
+void runFFSTest(FFS fn) {
+  static const size_t bpb = 8 * sizeof(uint64_t);
+  std::vector<uint64_t> data;
+  for (size_t nblocks = 1; nblocks <= 3; ++nblocks) {
+    size_t nbits = nblocks * bpb;
+    data.resize(nblocks);
+    auto begin = makeBitIterator(data.cbegin());
+    auto end = makeBitIterator(data.cend());
+    EXPECT_EQ(nbits, end - begin);
+    EXPECT_FALSE(begin == end);
+
+    // Try every possible combination of first bit set (including none),
+    // start bit, end bit
+    for (size_t firstSet = 0; firstSet <= nbits; ++firstSet) {
+      data.assign(nblocks, 0);
+      if (firstSet) {
+        size_t b = firstSet - 1;
+        data[b / bpb] |= (1ULL << (b % bpb));
+      }
+      for (size_t startBit = 0; startBit <= nbits; ++startBit) {
+        for (size_t endBit = startBit; endBit <= nbits; ++endBit) {
+          auto p = begin + startBit;
+          auto q = begin + endBit;
+          p = fn(p, q);
+          if (firstSet < startBit + 1 || firstSet >= endBit + 1) {
+            EXPECT_EQ(endBit, p - begin)
+              << "  firstSet=" << firstSet << " startBit=" << startBit
+              << " endBit=" << endBit << " nblocks=" << nblocks;
+          } else {
+            EXPECT_EQ(firstSet - 1, p - begin)
+              << "  firstSet=" << firstSet << " startBit=" << startBit
+              << " endBit=" << endBit << " nblocks=" << nblocks;
+          }
+        }
+      }
+    }
+  }
+}
+
+void runSimpleFFSTest(int iters) {
+  auto fn = simpleFFS<std::vector<uint64_t>::const_iterator>;
+  while (iters--) {
+    runFFSTest(fn);
+  }
+}
+
+void runRealFFSTest(int iters) {
+  auto fn = findFirstSet<std::vector<uint64_t>::const_iterator>;
+  while (iters--) {
+    runFFSTest(fn);
+  }
+}
+
+}
+
+TEST(BitIterator, SimpleFindFirstSet) {
+  runSimpleFFSTest(1);
+}
+
+TEST(BitIterator, FindFirstSet) {
+  runRealFFSTest(1);
+}
+
+BENCHMARK(SimpleFFSTest, iters) {
+  runSimpleFFSTest(iters);
+}
+BENCHMARK(RealFFSTest, iters) {
+  runRealFFSTest(iters);
+}
+
+/* --bm_min_iters=10 --bm_max_iters=100
+
+Benchmark                               Iters   Total t    t/iter iter/sec
+------------------------------------------------------------------------------
+runSimpleFFSTest                           10   4.82 s     482 ms  2.075
+runRealFFSTest                             19  2.011 s   105.9 ms  9.447
+
+*/
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  auto ret = RUN_ALL_TESTS();
+  if (!ret && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return ret;
+}
diff --git a/faux-folly/folly/test/BitsTest.cpp b/faux-folly/folly/test/BitsTest.cpp
new file mode 100644
index 0000000..528fc07
--- /dev/null
+++ b/faux-folly/folly/test/BitsTest.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Tudor Bosman (tudorb@fb.com)
+
+#include <gflags/gflags.h>
+#include <folly/Bits.h>
+#include <folly/Benchmark.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+// Test constexpr-ness.
+#ifndef __clang__
+static_assert(findFirstSet(2u) == 2, "findFirstSet");
+static_assert(findLastSet(2u) == 2, "findLastSet");
+static_assert(nextPowTwo(2u) == 2, "nextPowTwo");
+static_assert(isPowTwo(2u), "isPowTwo");
+#endif  // __clang__
+
+namespace {
+
+template <class INT>
+void testFFS() {
+  EXPECT_EQ(0, findFirstSet(static_cast<INT>(0)));
+  size_t bits = std::numeric_limits<
+    typename std::make_unsigned<INT>::type>::digits;
+  for (size_t i = 0; i < bits; i++) {
+    INT v = (static_cast<INT>(1) << (bits - 1)) |
+            (static_cast<INT>(1) << i);
+    EXPECT_EQ(i+1, findFirstSet(v));
+  }
+}
+
+template <class INT>
+void testFLS() {
+  typedef typename std::make_unsigned<INT>::type UINT;
+  EXPECT_EQ(0, findLastSet(static_cast<INT>(0)));
+  size_t bits = std::numeric_limits<UINT>::digits;
+  for (size_t i = 0; i < bits; i++) {
+    INT v1 = static_cast<UINT>(1) << i;
+    EXPECT_EQ(i + 1, findLastSet(v1));
+
+    INT v2 = (static_cast<UINT>(1) << i) - 1;
+    EXPECT_EQ(i, findLastSet(v2));
+  }
+}
+
+}  // namespace
+
+TEST(Bits, FindFirstSet) {
+  testFFS<char>();
+  testFFS<signed char>();
+  testFFS<unsigned char>();
+  testFFS<short>();
+  testFFS<unsigned short>();
+  testFFS<int>();
+  testFFS<unsigned int>();
+  testFFS<long>();
+  testFFS<unsigned long>();
+  testFFS<long long>();
+  testFFS<unsigned long long>();
+}
+
+TEST(Bits, FindLastSet) {
+  testFLS<char>();
+  testFLS<signed char>();
+  testFLS<unsigned char>();
+  testFLS<short>();
+  testFLS<unsigned short>();
+  testFLS<int>();
+  testFLS<unsigned int>();
+  testFLS<long>();
+  testFLS<unsigned long>();
+  testFLS<long long>();
+  testFLS<unsigned long long>();
+}
+
+#define testPowTwo(nextPowTwoFunc) {                              \
+  EXPECT_EQ(1, nextPowTwoFunc(0u));                               \
+  EXPECT_EQ(1, nextPowTwoFunc(1u));                               \
+  EXPECT_EQ(2, nextPowTwoFunc(2u));                               \
+  EXPECT_EQ(4, nextPowTwoFunc(3u));                               \
+  EXPECT_EQ(4, nextPowTwoFunc(4u));                               \
+  EXPECT_EQ(8, nextPowTwoFunc(5u));                               \
+  EXPECT_EQ(8, nextPowTwoFunc(6u));                               \
+  EXPECT_EQ(8, nextPowTwoFunc(7u));                               \
+  EXPECT_EQ(8, nextPowTwoFunc(8u));                               \
+  EXPECT_EQ(16, nextPowTwoFunc(9u));                              \
+  EXPECT_EQ(16, nextPowTwoFunc(13u));                             \
+  EXPECT_EQ(16, nextPowTwoFunc(16u));                             \
+  EXPECT_EQ(512, nextPowTwoFunc(510u));                           \
+  EXPECT_EQ(512, nextPowTwoFunc(511u));                           \
+  EXPECT_EQ(512, nextPowTwoFunc(512u));                           \
+  EXPECT_EQ(1024, nextPowTwoFunc(513u));                          \
+  EXPECT_EQ(1024, nextPowTwoFunc(777u));                          \
+  EXPECT_EQ(1ul << 31, nextPowTwoFunc((1ul << 31) - 1));          \
+  EXPECT_EQ(1ul << 32, nextPowTwoFunc((1ul << 32) - 1));          \
+  EXPECT_EQ(1ull << 63, nextPowTwoFunc((1ull << 62) + 1));        \
+}
+
+
+TEST(Bits, nextPowTwoClz) {
+  testPowTwo(nextPowTwo);
+}
+
+BENCHMARK(nextPowTwoClz, iters) {
+  for (unsigned long i = 0; i < iters; ++i) {
+    auto x = folly::nextPowTwo(iters);
+    folly::doNotOptimizeAway(x);
+  }
+}
+
+TEST(Bits, isPowTwo) {
+  EXPECT_FALSE(isPowTwo(0u));
+  EXPECT_TRUE(isPowTwo(1ul));
+  EXPECT_TRUE(isPowTwo(2ull));
+  EXPECT_FALSE(isPowTwo(3ul));
+  EXPECT_TRUE(isPowTwo(4ul));
+  EXPECT_FALSE(isPowTwo(5ul));
+  EXPECT_TRUE(isPowTwo(8ul));
+  EXPECT_FALSE(isPowTwo(15u));
+  EXPECT_TRUE(isPowTwo(16u));
+  EXPECT_FALSE(isPowTwo(17u));
+  EXPECT_FALSE(isPowTwo(511ul));
+  EXPECT_TRUE(isPowTwo(512ul));
+  EXPECT_FALSE(isPowTwo(513ul));
+  EXPECT_FALSE(isPowTwo((1ul<<31) - 1));
+  EXPECT_TRUE(isPowTwo(1ul<<31));
+  EXPECT_FALSE(isPowTwo((1ul<<31) + 1));
+  EXPECT_FALSE(isPowTwo((1ull<<63) - 1));
+  EXPECT_TRUE(isPowTwo(1ull<<63));
+  EXPECT_FALSE(isPowTwo((1ull<<63) + 1));
+}
+
+BENCHMARK_DRAW_LINE();
+BENCHMARK(isPowTwo, iters) {
+  bool b;
+  for (unsigned long i = 0; i < iters; ++i) {
+    b = folly::isPowTwo(i);
+    folly::doNotOptimizeAway(b);
+  }
+}
+
+TEST(Bits, popcount) {
+  EXPECT_EQ(0, popcount(0U));
+  EXPECT_EQ(1, popcount(1U));
+  EXPECT_EQ(32, popcount(uint32_t(-1)));
+  EXPECT_EQ(64, popcount(uint64_t(-1)));
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  auto ret = RUN_ALL_TESTS();
+  if (!ret && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return ret;
+}
+
+/*
+Benchmarks run on dual Xeon X5650's @ 2.67GHz w/hyperthreading enabled
+  (12 physical cores, 12 MB cache, 72 GB RAM)
+
+Benchmark                               Iters   Total t    t/iter iter/sec
+------------------------------------------------------------------------------
+*       nextPowTwoClz                 1000000  1.659 ms  1.659 ns  574.8 M
+*/
diff --git a/faux-folly/folly/test/CacheLocalityTest.cpp b/faux-folly/folly/test/CacheLocalityTest.cpp
new file mode 100644
index 0000000..fb40084
--- /dev/null
+++ b/faux-folly/folly/test/CacheLocalityTest.cpp
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2015 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/detail/CacheLocality.h>
+
+#include <sched.h>
+#include <memory>
+#include <thread>
+#include <type_traits>
+#include <unordered_map>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include <folly/Benchmark.h>
+
+using namespace folly::detail;
+
+/// This is the relevant nodes from a production box's sysfs tree.  If you
+/// think this map is ugly you should see the version of this test that
+/// used a real directory tree.  To reduce the chance of testing error
+/// I haven't tried to remove the common prefix
+static std::unordered_map<std::string,std::string> fakeSysfsTree = {
+  { "/sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_list", "0,17" },
+  { "/sys/devices/system/cpu/cpu0/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu0/cache/index1/shared_cpu_list", "0,17" },
+  { "/sys/devices/system/cpu/cpu0/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu0/cache/index2/shared_cpu_list", "0,17" },
+  { "/sys/devices/system/cpu/cpu0/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu0/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu1/cache/index0/shared_cpu_list", "1,18" },
+  { "/sys/devices/system/cpu/cpu1/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu1/cache/index1/shared_cpu_list", "1,18" },
+  { "/sys/devices/system/cpu/cpu1/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu1/cache/index2/shared_cpu_list", "1,18" },
+  { "/sys/devices/system/cpu/cpu1/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu1/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu1/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu2/cache/index0/shared_cpu_list", "2,19" },
+  { "/sys/devices/system/cpu/cpu2/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu2/cache/index1/shared_cpu_list", "2,19" },
+  { "/sys/devices/system/cpu/cpu2/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu2/cache/index2/shared_cpu_list", "2,19" },
+  { "/sys/devices/system/cpu/cpu2/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu2/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu2/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu3/cache/index0/shared_cpu_list", "3,20" },
+  { "/sys/devices/system/cpu/cpu3/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu3/cache/index1/shared_cpu_list", "3,20" },
+  { "/sys/devices/system/cpu/cpu3/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu3/cache/index2/shared_cpu_list", "3,20" },
+  { "/sys/devices/system/cpu/cpu3/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu3/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu3/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu4/cache/index0/shared_cpu_list", "4,21" },
+  { "/sys/devices/system/cpu/cpu4/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu4/cache/index1/shared_cpu_list", "4,21" },
+  { "/sys/devices/system/cpu/cpu4/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu4/cache/index2/shared_cpu_list", "4,21" },
+  { "/sys/devices/system/cpu/cpu4/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu4/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu4/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu5/cache/index0/shared_cpu_list", "5-6" },
+  { "/sys/devices/system/cpu/cpu5/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu5/cache/index1/shared_cpu_list", "5-6" },
+  { "/sys/devices/system/cpu/cpu5/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu5/cache/index2/shared_cpu_list", "5-6" },
+  { "/sys/devices/system/cpu/cpu5/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu5/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu5/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu6/cache/index0/shared_cpu_list", "5-6" },
+  { "/sys/devices/system/cpu/cpu6/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu6/cache/index1/shared_cpu_list", "5-6" },
+  { "/sys/devices/system/cpu/cpu6/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu6/cache/index2/shared_cpu_list", "5-6" },
+  { "/sys/devices/system/cpu/cpu6/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu6/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu6/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu7/cache/index0/shared_cpu_list", "7,22" },
+  { "/sys/devices/system/cpu/cpu7/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu7/cache/index1/shared_cpu_list", "7,22" },
+  { "/sys/devices/system/cpu/cpu7/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu7/cache/index2/shared_cpu_list", "7,22" },
+  { "/sys/devices/system/cpu/cpu7/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu7/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu7/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu8/cache/index0/shared_cpu_list", "8,23" },
+  { "/sys/devices/system/cpu/cpu8/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu8/cache/index1/shared_cpu_list", "8,23" },
+  { "/sys/devices/system/cpu/cpu8/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu8/cache/index2/shared_cpu_list", "8,23" },
+  { "/sys/devices/system/cpu/cpu8/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu8/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu8/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu9/cache/index0/shared_cpu_list", "9,24" },
+  { "/sys/devices/system/cpu/cpu9/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu9/cache/index1/shared_cpu_list", "9,24" },
+  { "/sys/devices/system/cpu/cpu9/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu9/cache/index2/shared_cpu_list", "9,24" },
+  { "/sys/devices/system/cpu/cpu9/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu9/cache/index3/shared_cpu_list", "9-16,24-31" },
+  { "/sys/devices/system/cpu/cpu9/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu10/cache/index0/shared_cpu_list", "10,25" },
+  { "/sys/devices/system/cpu/cpu10/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu10/cache/index1/shared_cpu_list", "10,25" },
+  { "/sys/devices/system/cpu/cpu10/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu10/cache/index2/shared_cpu_list", "10,25" },
+  { "/sys/devices/system/cpu/cpu10/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu10/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu10/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu11/cache/index0/shared_cpu_list", "11,26" },
+  { "/sys/devices/system/cpu/cpu11/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu11/cache/index1/shared_cpu_list", "11,26" },
+  { "/sys/devices/system/cpu/cpu11/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu11/cache/index2/shared_cpu_list", "11,26" },
+  { "/sys/devices/system/cpu/cpu11/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu11/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu11/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu12/cache/index0/shared_cpu_list", "12,27" },
+  { "/sys/devices/system/cpu/cpu12/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu12/cache/index1/shared_cpu_list", "12,27" },
+  { "/sys/devices/system/cpu/cpu12/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu12/cache/index2/shared_cpu_list", "12,27" },
+  { "/sys/devices/system/cpu/cpu12/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu12/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu12/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu13/cache/index0/shared_cpu_list", "13,28" },
+  { "/sys/devices/system/cpu/cpu13/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu13/cache/index1/shared_cpu_list", "13,28" },
+  { "/sys/devices/system/cpu/cpu13/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu13/cache/index2/shared_cpu_list", "13,28" },
+  { "/sys/devices/system/cpu/cpu13/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu13/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu13/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu14/cache/index0/shared_cpu_list", "14,29" },
+  { "/sys/devices/system/cpu/cpu14/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu14/cache/index1/shared_cpu_list", "14,29" },
+  { "/sys/devices/system/cpu/cpu14/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu14/cache/index2/shared_cpu_list", "14,29" },
+  { "/sys/devices/system/cpu/cpu14/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu14/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu14/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu15/cache/index0/shared_cpu_list", "15,30" },
+  { "/sys/devices/system/cpu/cpu15/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu15/cache/index1/shared_cpu_list", "15,30" },
+  { "/sys/devices/system/cpu/cpu15/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu15/cache/index2/shared_cpu_list", "15,30" },
+  { "/sys/devices/system/cpu/cpu15/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu15/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu15/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu16/cache/index0/shared_cpu_list", "16,31" },
+  { "/sys/devices/system/cpu/cpu16/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu16/cache/index1/shared_cpu_list", "16,31" },
+  { "/sys/devices/system/cpu/cpu16/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu16/cache/index2/shared_cpu_list", "16,31" },
+  { "/sys/devices/system/cpu/cpu16/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu16/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu16/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu17/cache/index0/shared_cpu_list", "0,17" },
+  { "/sys/devices/system/cpu/cpu17/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu17/cache/index1/shared_cpu_list", "0,17" },
+  { "/sys/devices/system/cpu/cpu17/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu17/cache/index2/shared_cpu_list", "0,17" },
+  { "/sys/devices/system/cpu/cpu17/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu17/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu17/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu18/cache/index0/shared_cpu_list", "1,18" },
+  { "/sys/devices/system/cpu/cpu18/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu18/cache/index1/shared_cpu_list", "1,18" },
+  { "/sys/devices/system/cpu/cpu18/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu18/cache/index2/shared_cpu_list", "1,18" },
+  { "/sys/devices/system/cpu/cpu18/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu18/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu18/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu19/cache/index0/shared_cpu_list", "2,19" },
+  { "/sys/devices/system/cpu/cpu19/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu19/cache/index1/shared_cpu_list", "2,19" },
+  { "/sys/devices/system/cpu/cpu19/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu19/cache/index2/shared_cpu_list", "2,19" },
+  { "/sys/devices/system/cpu/cpu19/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu19/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu19/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu20/cache/index0/shared_cpu_list", "3,20" },
+  { "/sys/devices/system/cpu/cpu20/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu20/cache/index1/shared_cpu_list", "3,20" },
+  { "/sys/devices/system/cpu/cpu20/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu20/cache/index2/shared_cpu_list", "3,20" },
+  { "/sys/devices/system/cpu/cpu20/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu20/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu20/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu21/cache/index0/shared_cpu_list", "4,21" },
+  { "/sys/devices/system/cpu/cpu21/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu21/cache/index1/shared_cpu_list", "4,21" },
+  { "/sys/devices/system/cpu/cpu21/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu21/cache/index2/shared_cpu_list", "4,21" },
+  { "/sys/devices/system/cpu/cpu21/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu21/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu21/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu22/cache/index0/shared_cpu_list", "7,22" },
+  { "/sys/devices/system/cpu/cpu22/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu22/cache/index1/shared_cpu_list", "7,22" },
+  { "/sys/devices/system/cpu/cpu22/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu22/cache/index2/shared_cpu_list", "7,22" },
+  { "/sys/devices/system/cpu/cpu22/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu22/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu22/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu23/cache/index0/shared_cpu_list", "8,23" },
+  { "/sys/devices/system/cpu/cpu23/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu23/cache/index1/shared_cpu_list", "8,23" },
+  { "/sys/devices/system/cpu/cpu23/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_list", "8,23" },
+  { "/sys/devices/system/cpu/cpu23/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu23/cache/index3/shared_cpu_list", "0-8,17-23" },
+  { "/sys/devices/system/cpu/cpu23/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu24/cache/index0/shared_cpu_list", "9,24" },
+  { "/sys/devices/system/cpu/cpu24/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu24/cache/index1/shared_cpu_list", "9,24" },
+  { "/sys/devices/system/cpu/cpu24/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu24/cache/index2/shared_cpu_list", "9,24" },
+  { "/sys/devices/system/cpu/cpu24/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu24/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu24/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu25/cache/index0/shared_cpu_list", "10,25" },
+  { "/sys/devices/system/cpu/cpu25/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu25/cache/index1/shared_cpu_list", "10,25" },
+  { "/sys/devices/system/cpu/cpu25/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu25/cache/index2/shared_cpu_list", "10,25" },
+  { "/sys/devices/system/cpu/cpu25/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu25/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu25/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu26/cache/index0/shared_cpu_list", "11,26" },
+  { "/sys/devices/system/cpu/cpu26/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu26/cache/index1/shared_cpu_list", "11,26" },
+  { "/sys/devices/system/cpu/cpu26/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu26/cache/index2/shared_cpu_list", "11,26" },
+  { "/sys/devices/system/cpu/cpu26/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu26/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu26/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu27/cache/index0/shared_cpu_list", "12,27" },
+  { "/sys/devices/system/cpu/cpu27/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu27/cache/index1/shared_cpu_list", "12,27" },
+  { "/sys/devices/system/cpu/cpu27/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu27/cache/index2/shared_cpu_list", "12,27" },
+  { "/sys/devices/system/cpu/cpu27/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu27/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu27/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu28/cache/index0/shared_cpu_list", "13,28" },
+  { "/sys/devices/system/cpu/cpu28/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu28/cache/index1/shared_cpu_list", "13,28" },
+  { "/sys/devices/system/cpu/cpu28/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu28/cache/index2/shared_cpu_list", "13,28" },
+  { "/sys/devices/system/cpu/cpu28/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu28/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu28/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu29/cache/index0/shared_cpu_list", "14,29" },
+  { "/sys/devices/system/cpu/cpu29/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu29/cache/index1/shared_cpu_list", "14,29" },
+  { "/sys/devices/system/cpu/cpu29/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu29/cache/index2/shared_cpu_list", "14,29" },
+  { "/sys/devices/system/cpu/cpu29/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu29/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu29/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu30/cache/index0/shared_cpu_list", "15,30" },
+  { "/sys/devices/system/cpu/cpu30/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu30/cache/index1/shared_cpu_list", "15,30" },
+  { "/sys/devices/system/cpu/cpu30/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu30/cache/index2/shared_cpu_list", "15,30" },
+  { "/sys/devices/system/cpu/cpu30/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu30/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu30/cache/index3/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu31/cache/index0/shared_cpu_list", "16,31" },
+  { "/sys/devices/system/cpu/cpu31/cache/index0/type", "Data" },
+  { "/sys/devices/system/cpu/cpu31/cache/index1/shared_cpu_list", "16,31" },
+  { "/sys/devices/system/cpu/cpu31/cache/index1/type", "Instruction" },
+  { "/sys/devices/system/cpu/cpu31/cache/index2/shared_cpu_list", "16,31" },
+  { "/sys/devices/system/cpu/cpu31/cache/index2/type", "Unified" },
+  { "/sys/devices/system/cpu/cpu31/cache/index3/shared_cpu_list", "9-16,24-31"},
+  { "/sys/devices/system/cpu/cpu31/cache/index3/type", "Unified" }
+};
+
+/// This is the expected CacheLocality structure for fakeSysfsTree
+static const CacheLocality nonUniformExampleLocality = {
+  32,
+  { 16, 16, 2 },
+  { 0, 2, 4, 6, 8, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28,
+    30, 1, 3, 5, 7, 9, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 }
+};
+
+TEST(CacheLocality, FakeSysfs) {
+  auto parsed = CacheLocality::readFromSysfsTree([](std::string name) {
+    auto iter = fakeSysfsTree.find(name);
+    return iter == fakeSysfsTree.end() ? std::string() : iter->second;
+  });
+
+  auto& expected = nonUniformExampleLocality;
+  EXPECT_EQ(expected.numCpus, parsed.numCpus);
+  EXPECT_EQ(expected.numCachesByLevel, parsed.numCachesByLevel);
+  EXPECT_EQ(expected.localityIndexByCpu, parsed.localityIndexByCpu);
+}
+
+TEST(Getcpu, VdsoGetcpu) {
+  unsigned cpu;
+  Getcpu::vdsoFunc()(&cpu, nullptr, nullptr);
+
+  EXPECT_TRUE(cpu < CPU_SETSIZE);
+}
+
+TEST(SequentialThreadId, Simple) {
+  unsigned cpu = 0;
+  auto rv = SequentialThreadId<std::atomic>::getcpu(&cpu, nullptr, nullptr);
+  EXPECT_EQ(rv, 0);
+  EXPECT_TRUE(cpu > 0);
+  unsigned again;
+  SequentialThreadId<std::atomic>::getcpu(&again, nullptr, nullptr);
+  EXPECT_EQ(cpu, again);
+}
+
+static FOLLY_TLS unsigned testingCpu = 0;
+
+static int testingGetcpu(unsigned* cpu, unsigned* node, void* unused) {
+  if (cpu != nullptr) {
+    *cpu = testingCpu;
+  }
+  if (node != nullptr) {
+    *node = testingCpu;
+  }
+  return 0;
+}
+
+TEST(AccessSpreader, Stubbed) {
+  std::vector<std::unique_ptr<AccessSpreader<>>> spreaders(100);
+  for (size_t s = 1; s < spreaders.size(); ++s) {
+    spreaders[s].reset(new AccessSpreader<>(
+        s, nonUniformExampleLocality, &testingGetcpu));
+  }
+  std::vector<size_t> cpusInLocalityOrder = {
+      0, 17, 1, 18, 2, 19, 3, 20, 4, 21, 5, 6, 7, 22, 8, 23, 9, 24, 10, 25,
+      11, 26, 12, 27, 13, 28, 14, 29, 15, 30, 16, 31 };
+  for (size_t i = 0; i < 32; ++i) {
+    // extra i * 32 is to check wrapping behavior of impl
+    testingCpu = cpusInLocalityOrder[i] + i * 64;
+    for (size_t s = 1; s < spreaders.size(); ++s) {
+      EXPECT_EQ((i * s) / 32, spreaders[s]->current())
+          << "i=" << i << ", cpu=" << testingCpu << ", s=" << s;
+    }
+  }
+}
+
+TEST(AccessSpreader, Default) {
+  AccessSpreader<> spreader(16);
+  EXPECT_LT(spreader.current(), 16);
+}
+
+TEST(AccessSpreader, Shared) {
+  for (size_t s = 1; s < 200; ++s) {
+    EXPECT_LT(AccessSpreader<>::shared(s).current(), s);
+  }
+}
+
+TEST(AccessSpreader, Statics) {
+  LOG(INFO) << "stripeByCore.numStripes() = "
+            << AccessSpreader<>::stripeByCore.numStripes();
+  LOG(INFO) << "stripeByChip.numStripes() = "
+            << AccessSpreader<>::stripeByChip.numStripes();
+  for (size_t s = 1; s < 200; ++s) {
+    EXPECT_LT(AccessSpreader<>::current(s), s);
+  }
+}
+
+TEST(AccessSpreader, Wrapping) {
+  // this test won't pass unless locality.numCpus divides kMaxCpus
+  auto numCpus = 16;
+  auto locality = CacheLocality::uniform(numCpus);
+  for (size_t s = 1; s < 200; ++s) {
+    AccessSpreader<> spreader(s, locality, &testingGetcpu);
+    for (size_t c = 0; c < 400; ++c) {
+      testingCpu = c;
+      auto observed = spreader.current();
+      testingCpu = c % numCpus;
+      auto expected = spreader.current();
+      EXPECT_EQ(expected, observed)
+          << "numCpus=" << numCpus << ", s=" << s << ", c=" << c;
+    }
+  }
+}
+
+// Benchmarked at ~21 nanos on fbk35 (2.6) and fbk18 (3.2) kernels with
+// a 2.2Ghz Xeon
+// ============================================================================
+// folly/test/CacheLocalityTest.cpp                relative  time/iter  iters/s
+// ============================================================================
+// LocalAccessSpreaderUse                                      20.77ns   48.16M
+// SharedAccessSpreaderUse                                     21.95ns   45.55M
+// AccessSpreaderConstruction                                 466.56ns    2.14M
+// ============================================================================
+
+BENCHMARK(LocalAccessSpreaderUse, iters) {
+  folly::BenchmarkSuspender braces;
+  AccessSpreader<> spreader(16);
+  braces.dismiss();
+
+  for (unsigned long i = 0; i < iters; ++i) {
+    auto x = spreader.current();
+    folly::doNotOptimizeAway(x);
+  }
+}
+
+BENCHMARK(SharedAccessSpreaderUse, iters) {
+  for (unsigned long i = 0; i < iters; ++i) {
+    auto x = AccessSpreader<>::current(16);
+    folly::doNotOptimizeAway(x);
+  }
+}
+
+BENCHMARK(AccessSpreaderConstruction, iters) {
+  std::aligned_storage<sizeof(AccessSpreader<>),
+                       std::alignment_of<AccessSpreader<>>::value>::type raw;
+  for (unsigned long i = 0; i < iters; ++i) {
+    auto x = new (&raw) AccessSpreader<>(16);
+    folly::doNotOptimizeAway(x);
+    x->~AccessSpreader();
+  }
+}
+
+enum class SpreaderType { GETCPU, SHARED, TLS_RR };
+
+// Benchmark scores here reflect the time for 32 threads to perform an
+// atomic increment on a dual-socket E5-2660 @ 2.2Ghz.  Surprisingly,
+// if we don't separate the counters onto unique 128 byte stripes the
+// 1_stripe and 2_stripe results are identical, even though the L3 is
+// claimed to have 64 byte cache lines.
+//
+// _stub means there was no call to getcpu or the tls round-robin
+// implementation, because for a single stripe the cpu doesn't matter.
+// _getcpu refers to the vdso getcpu implementation with a locally
+// constructed AccessSpreader.  _tls_rr refers to execution using
+// SequentialThreadId, the fallback if the vdso getcpu isn't available.
+// _shared refers to calling AccessSpreader<>::current(numStripes)
+// inside the hot loop.
+//
+// At 16_stripe_0_work and 32_stripe_0_work there is only L1 traffic,
+// so since the stripe selection is 21 nanos the atomic increments in
+// the L1 is ~15 nanos.  At width 8_stripe_0_work the line is expected
+// to ping-pong almost every operation, since the loops have the same
+// duration.  Widths 4 and 2 have the same behavior, but each tour of the
+// cache line is 4 and 8 cores long, respectively.  These all suggest a
+// lower bound of 60 nanos for intra-chip handoff and increment between
+// the L1s.
+//
+// With 455 nanos (1K cycles) of busywork per contended increment, the
+// system can hide all of the latency of a tour of length 4, but not
+// quite one of length 8.  I was a bit surprised at how much worse the
+// non-striped version got.  It seems that the inter-chip traffic also
+// interferes with the L1-only localWork.load().  When the local work is
+// doubled to about 1 microsecond we see that the inter-chip contention
+// is still very important, but subdivisions on the same chip don't matter.
+//
+// sudo nice -n -20
+//   _bin/folly/test/cache_locality_test --benchmark --bm_min_iters=1000000
+// ============================================================================
+// folly/test/CacheLocalityTest.cpp                relative  time/iter  iters/s
+// ============================================================================
+// contentionAtWidth(1_stripe_0_work_stub)                      1.14us  873.64K
+// contentionAtWidth(2_stripe_0_work_getcpu)                  495.58ns    2.02M
+// contentionAtWidth(4_stripe_0_work_getcpu)                  232.99ns    4.29M
+// contentionAtWidth(8_stripe_0_work_getcpu)                  101.16ns    9.88M
+// contentionAtWidth(16_stripe_0_work_getcpu)                  41.93ns   23.85M
+// contentionAtWidth(32_stripe_0_work_getcpu)                  42.04ns   23.79M
+// contentionAtWidth(64_stripe_0_work_getcpu)                  41.94ns   23.84M
+// contentionAtWidth(2_stripe_0_work_tls_rr)                    1.00us  997.41K
+// contentionAtWidth(4_stripe_0_work_tls_rr)                  694.41ns    1.44M
+// contentionAtWidth(8_stripe_0_work_tls_rr)                  590.27ns    1.69M
+// contentionAtWidth(16_stripe_0_work_tls_rr)                 222.13ns    4.50M
+// contentionAtWidth(32_stripe_0_work_tls_rr)                 169.49ns    5.90M
+// contentionAtWidth(64_stripe_0_work_tls_rr)                 162.20ns    6.17M
+// contentionAtWidth(2_stripe_0_work_shared)                  495.54ns    2.02M
+// contentionAtWidth(4_stripe_0_work_shared)                  236.27ns    4.23M
+// contentionAtWidth(8_stripe_0_work_shared)                  114.81ns    8.71M
+// contentionAtWidth(16_stripe_0_work_shared)                  44.65ns   22.40M
+// contentionAtWidth(32_stripe_0_work_shared)                  41.76ns   23.94M
+// contentionAtWidth(64_stripe_0_work_shared)                  43.47ns   23.00M
+// atomicIncrBaseline(local_incr_0_work)                       20.39ns   49.06M
+// ----------------------------------------------------------------------------
+// contentionAtWidth(1_stripe_500_work_stub)                    2.04us  491.13K
+// contentionAtWidth(2_stripe_500_work_getcpu)                610.98ns    1.64M
+// contentionAtWidth(4_stripe_500_work_getcpu)                507.72ns    1.97M
+// contentionAtWidth(8_stripe_500_work_getcpu)                542.53ns    1.84M
+// contentionAtWidth(16_stripe_500_work_getcpu)               496.55ns    2.01M
+// contentionAtWidth(32_stripe_500_work_getcpu)               500.67ns    2.00M
+// atomicIncrBaseline(local_incr_500_work)                    484.69ns    2.06M
+// ----------------------------------------------------------------------------
+// contentionAtWidth(1_stripe_1000_work_stub)                   2.11us  473.78K
+// contentionAtWidth(2_stripe_1000_work_getcpu)               970.64ns    1.03M
+// contentionAtWidth(4_stripe_1000_work_getcpu)               987.31ns    1.01M
+// contentionAtWidth(8_stripe_1000_work_getcpu)                 1.01us  985.52K
+// contentionAtWidth(16_stripe_1000_work_getcpu)              986.09ns    1.01M
+// contentionAtWidth(32_stripe_1000_work_getcpu)              960.23ns    1.04M
+// atomicIncrBaseline(local_incr_1000_work)                   950.63ns    1.05M
+// ============================================================================
+static void contentionAtWidth(size_t iters, size_t stripes, size_t work,
+                              SpreaderType spreaderType,
+                              size_t counterAlignment = 128,
+                              size_t numThreads = 32) {
+  folly::BenchmarkSuspender braces;
+
+  AccessSpreader<> spreader(
+      stripes,
+      CacheLocality::system<std::atomic>(),
+      spreaderType == SpreaderType::TLS_RR
+          ? SequentialThreadId<std::atomic>::getcpu : nullptr);
+
+  std::atomic<size_t> ready(0);
+  std::atomic<bool> go(false);
+
+  // while in theory the cache line size is 64 bytes, experiments show
+  // that we get contention on 128 byte boundaries for Ivy Bridge.  The
+  // extra indirection adds 1 or 2 nanos
+  assert(counterAlignment >= sizeof(std::atomic<size_t>));
+  std::vector<char> raw(counterAlignment * stripes);
+
+  // if we happen to be using the tlsRoundRobin, then sequentially
+  // assigning the thread identifiers is the unlikely best-case scenario.
+  // We don't want to unfairly benefit or penalize.  Computing the exact
+  // maximum likelihood of the probability distributions is annoying, so
+  // I approximate as 2/5 of the ids that have no threads, 2/5 that have
+  // 1, 2/15 that have 2, and 1/15 that have 3.  We accomplish this by
+  // wrapping back to slot 0 when we hit 1/15 and 1/5.
+
+  std::vector<std::thread> threads;
+  while (threads.size() < numThreads) {
+    threads.push_back(std::thread([&,iters,stripes,work]() {
+      std::atomic<size_t>* counters[stripes];
+      for (size_t i = 0; i < stripes; ++i) {
+        counters[i]
+          = new (raw.data() + counterAlignment * i) std::atomic<size_t>();
+      }
+
+      spreader.current();
+      ready++;
+      while (!go.load()) {
+        sched_yield();
+      }
+      std::atomic<int> localWork(0);
+      if (spreaderType == SpreaderType::SHARED) {
+        for (size_t i = iters; i > 0; --i) {
+          ++*(counters[AccessSpreader<>::current(stripes)]);
+          for (size_t j = work; j > 0; --j) {
+            localWork.load();
+          }
+        }
+      } else {
+        for (size_t i = iters; i > 0; --i) {
+          ++*(counters[spreader.current()]);
+          for (size_t j = work; j > 0; --j) {
+            localWork.load();
+          }
+        }
+      }
+    }));
+
+    if (threads.size() == numThreads / 15 ||
+        threads.size() == numThreads / 5) {
+      // create a few dummy threads to wrap back around to 0 mod numCpus
+      for (size_t i = threads.size(); i != numThreads; ++i) {
+        std::thread([&]() {
+          spreader.current();
+        }).join();
+      }
+    }
+  }
+
+  while (ready < numThreads) {
+    sched_yield();
+  }
+  braces.dismiss();
+  go = true;
+
+  for (auto& thr : threads) {
+    thr.join();
+  }
+}
+
+static void atomicIncrBaseline(size_t iters, size_t work,
+                               size_t numThreads = 32) {
+  folly::BenchmarkSuspender braces;
+
+  std::atomic<bool> go(false);
+
+  std::vector<std::thread> threads;
+  while (threads.size() < numThreads) {
+    threads.push_back(std::thread([&]() {
+      while (!go.load()) {
+        sched_yield();
+      }
+      std::atomic<size_t> localCounter(0);
+      std::atomic<int> localWork(0);
+      for (size_t i = iters; i > 0; --i) {
+        localCounter++;
+        for (size_t j = work; j > 0; --j) {
+          localWork.load();
+        }
+      }
+    }));
+  }
+
+  braces.dismiss();
+  go = true;
+
+  for (auto& thr : threads) {
+    thr.join();
+  }
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 1_stripe_0_work_stub,
+                      1, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 2_stripe_0_work_getcpu,
+                      2, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 4_stripe_0_work_getcpu,
+                      4, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 8_stripe_0_work_getcpu,
+                      8, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 16_stripe_0_work_getcpu,
+                      16, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 32_stripe_0_work_getcpu,
+                      32, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 64_stripe_0_work_getcpu,
+                      64, 0, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 2_stripe_0_work_tls_rr,
+                      2, 0, SpreaderType::TLS_RR)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 4_stripe_0_work_tls_rr,
+                      4, 0, SpreaderType::TLS_RR)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 8_stripe_0_work_tls_rr,
+                      8, 0, SpreaderType::TLS_RR)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 16_stripe_0_work_tls_rr,
+                      16, 0, SpreaderType::TLS_RR)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 32_stripe_0_work_tls_rr,
+                      32, 0, SpreaderType::TLS_RR)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 64_stripe_0_work_tls_rr,
+                      64, 0, SpreaderType::TLS_RR)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 2_stripe_0_work_shared,
+                      2, 0, SpreaderType::SHARED)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 4_stripe_0_work_shared,
+                      4, 0, SpreaderType::SHARED)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 8_stripe_0_work_shared,
+                      8, 0, SpreaderType::SHARED)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 16_stripe_0_work_shared,
+                      16, 0, SpreaderType::SHARED)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 32_stripe_0_work_shared,
+                      32, 0, SpreaderType::SHARED)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 64_stripe_0_work_shared,
+                      64, 0, SpreaderType::SHARED)
+BENCHMARK_NAMED_PARAM(atomicIncrBaseline, local_incr_0_work, 0)
+BENCHMARK_DRAW_LINE()
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 1_stripe_500_work_stub,
+                      1, 500, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 2_stripe_500_work_getcpu,
+                      2, 500, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 4_stripe_500_work_getcpu,
+                      4, 500, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 8_stripe_500_work_getcpu,
+                      8, 500, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 16_stripe_500_work_getcpu,
+                      16, 500, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 32_stripe_500_work_getcpu,
+                      32, 500, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(atomicIncrBaseline, local_incr_500_work, 500)
+BENCHMARK_DRAW_LINE()
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 1_stripe_1000_work_stub,
+                      1, 1000, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 2_stripe_1000_work_getcpu,
+                      2, 1000, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 4_stripe_1000_work_getcpu,
+                      4, 1000, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 8_stripe_1000_work_getcpu,
+                      8, 1000, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 16_stripe_1000_work_getcpu,
+                      16, 1000, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(contentionAtWidth, 32_stripe_1000_work_getcpu,
+                      32, 1000, SpreaderType::GETCPU)
+BENCHMARK_NAMED_PARAM(atomicIncrBaseline, local_incr_1000_work, 1000)
+
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  auto ret = RUN_ALL_TESTS();
+  if (!ret && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return ret;
+}
diff --git a/faux-folly/folly/test/CancellationTest.cpp b/faux-folly/folly/test/CancellationTest.cpp
new file mode 100644
index 0000000..9cc89ae
--- /dev/null
+++ b/faux-folly/folly/test/CancellationTest.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2015 Nest Labs, 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.
+ */
+
+// Test bed for folly/Cancellation.h
+
+#include <folly/Cancellation.h>
+#include <gtest/gtest.h>
+#include <future>
+
+using ::folly::Cancellation;
+
+/* Tests the basic operation of the construct */
+TEST(Cancellation, BasicFunction)
+{
+    Cancellation c;
+    EXPECT_FALSE(c.is_cancelled());
+    c.cancel();
+    EXPECT_TRUE(c.is_cancelled());
+}
+
+/* Independently constructed objects shall not share state */
+TEST(Cancellation, Independence)
+{
+    Cancellation c0;
+    Cancellation c1;
+    EXPECT_FALSE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+    c0.cancel();
+    EXPECT_TRUE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+}
+
+/* Objects using copy constructor shall have a common state */
+TEST(Cancellation, CopyConstructor)
+{
+    Cancellation c0;
+    Cancellation c1(c0);
+    EXPECT_FALSE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+    c0.cancel();
+    EXPECT_TRUE(c0.is_cancelled());
+    EXPECT_TRUE(c1.is_cancelled());
+}
+
+/* Objects using assignment operator shall have a common state */
+TEST(Cancellation, AssignmentOperator0)
+{
+    Cancellation c0;
+    Cancellation c1;
+    c0 = c1;
+    EXPECT_FALSE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+    c0.cancel();
+    EXPECT_TRUE(c0.is_cancelled());
+    EXPECT_TRUE(c1.is_cancelled());
+}
+
+/* Objects using assignment operator shall have a common state, even after calling cancel() */
+TEST(Cancellation, AssignmentOperator1)
+{
+    Cancellation c0;
+    Cancellation c1;
+    EXPECT_FALSE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+    c1.cancel();
+    c0 = c1;
+    EXPECT_TRUE(c0.is_cancelled());
+    EXPECT_TRUE(c1.is_cancelled());
+}
+
+/* Objects using move operator shall transfer state */
+TEST(Cancellation, MoveConstructor0)
+{
+    Cancellation c0;
+    EXPECT_FALSE(c0.is_cancelled());
+    Cancellation c1(std::move(c0));
+    EXPECT_FALSE(c1.is_cancelled());
+}
+
+/* Objects using move operator shall transfer state */
+TEST(Cancellation, MoveConstructor1)
+{
+    Cancellation c0;
+    EXPECT_FALSE(c0.is_cancelled());
+    c0.cancel();
+    EXPECT_TRUE(c0.is_cancelled());
+    Cancellation c1(std::move(c0));
+    EXPECT_TRUE(c1.is_cancelled());
+}
+
+/* Objects using move assignment shall transfer state */
+TEST(Cancellation, MoveAssignment0)
+{
+    Cancellation c0;
+    EXPECT_FALSE(c0.is_cancelled());
+    Cancellation c1;
+    c1.cancel();
+    EXPECT_TRUE(c1.is_cancelled());
+    c1 = std::move(c0);
+    EXPECT_FALSE(c1.is_cancelled());
+}
+
+/* Objects using move assignment shall transfer state */
+TEST(Cancellation, MoveAssignment1)
+{
+    Cancellation c0;
+    Cancellation c1;
+    EXPECT_FALSE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+    c0.cancel();
+    EXPECT_TRUE(c0.is_cancelled());
+    EXPECT_FALSE(c1.is_cancelled());
+    c0 = std::move(c1);
+    EXPECT_FALSE(c0.is_cancelled());
+}
+
+/* CancellationSharedState shall be a basic lockable type */
+TEST(CancellationSharedState, LockableInterface)
+{
+    folly::detail::CancellationSharedState cp;
+    cp.lock();
+    cp.unlock();
+    EXPECT_TRUE(true);
+}
+
+/* CancellationStateLock shall acquire lock if not cancelled */
+TEST(CancellationSharedState, StateLockAcquire)
+{
+    Cancellation c;
+    EXPECT_FALSE(c.is_cancelled());
+    auto h = c.hold_state();
+    EXPECT_TRUE(h.owns_lock());
+    EXPECT_TRUE(static_cast<bool>(h));
+}
+
+/* CancellationStateLock shall not acquire lock if cancelled */
+TEST(CancellationSharedState, StateLockDenied)
+{
+    Cancellation c;
+    c.cancel();
+    EXPECT_TRUE(c.is_cancelled());
+    auto h = c.hold_state();
+    EXPECT_FALSE(h.owns_lock());
+    EXPECT_FALSE(static_cast<bool>(h));
+}
+
+/* Multiple CancellationStateLock's may be held without blocking */
+TEST(CancellationStateLock, MultiReader)
+{
+    Cancellation c;
+    auto h0 = c.hold_state();
+    EXPECT_TRUE((bool)h0);
+    auto h1 = c.hold_state();
+    EXPECT_TRUE((bool)h1);
+    EXPECT_FALSE(c.is_cancelled());
+    h0.unlock();
+    EXPECT_FALSE(h0);
+    h1.unlock();
+    EXPECT_FALSE(h1);
+    c.cancel();
+}
+
+/* CancellationStateLock shall be RAII */
+TEST(CancellationStateLock, RAII)
+{
+    Cancellation c;
+    {
+        auto h = c.hold_state();
+        EXPECT_TRUE((bool)h);
+    }
+    c.cancel();
+    {
+        auto h = c.hold_state();
+        EXPECT_FALSE(h);
+    }
+}
+
+/* cancel operation shall block a cancel until all holds release */
+TEST(CancellationStateLock, BlocksCancel)
+{
+    Cancellation c;
+    auto h0 = c.hold_state();
+    auto h1 = c.hold_state();
+    auto h2 = c.hold_state();
+    auto h3 = c.hold_state();
+    auto h4 = c.hold_state();
+
+    EXPECT_TRUE((bool)h0);
+    EXPECT_TRUE((bool)h1);
+    EXPECT_TRUE((bool)h2);
+    EXPECT_TRUE((bool)h3);
+    EXPECT_TRUE((bool)h4);
+    EXPECT_FALSE(c.is_cancelled());
+
+    std::mutex mx;
+    std::condition_variable cond;
+    bool ready = false;
+    auto fut = std::async(std::launch::async,
+                          [&] {
+                              std::unique_lock<std::mutex> lock(mx);
+                              ready = true;
+                              cond.notify_one();
+                              lock.unlock();
+
+                              c.cancel();
+                          });
+
+    std::unique_lock<std::mutex> lock(mx);
+    while (!ready) {
+        cond.wait(lock);
+    }
+    lock.unlock();
+
+    /* while adding a sleep right here would ensure that the remote thread
+     * is blocked... defer that to the BlocksMultiCancel test
+     */
+
+    EXPECT_FALSE(c.is_cancelled());
+    h0.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h1.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h2.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h3.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h4.unlock();
+
+    /* state is indeterminate until thread completes */
+
+    fut.wait();
+
+    EXPECT_TRUE(c.is_cancelled());
+}
+
+/* cancel operation shall block multiple cancellations until all holds release */
+TEST(CancellationStateLock, BlocksMultiCancel)
+{
+    Cancellation c;
+    auto h0 = c.hold_state();
+    auto h1 = c.hold_state();
+    auto h2 = c.hold_state();
+    auto h3 = c.hold_state();
+    auto h4 = c.hold_state();
+
+    EXPECT_TRUE((bool)h0);
+    EXPECT_TRUE((bool)h1);
+    EXPECT_TRUE((bool)h2);
+    EXPECT_TRUE((bool)h3);
+    EXPECT_TRUE((bool)h4);
+    EXPECT_FALSE(c.is_cancelled());
+
+    std::mutex mx;
+    std::condition_variable cond;
+    unsigned ready {0};
+    auto lam = [&] {
+        std::unique_lock<std::mutex> lock(mx);
+        ++ready;
+        cond.notify_one();
+        lock.unlock();
+
+        c.cancel();
+    };
+    auto fut0 = std::async(std::launch::async, lam);
+    auto fut1 = std::async(std::launch::async, lam);
+    auto fut2 = std::async(std::launch::async, lam);
+    auto fut3 = std::async(std::launch::async, lam);
+    auto fut4 = std::async(std::launch::async, lam);
+
+    std::unique_lock<std::mutex> lock(mx);
+    while (ready < 5) {
+        cond.wait(lock);
+    }
+    lock.unlock();
+
+    /* try to ensure remote threads are /really/ blocked */
+    usleep(40000);
+
+    EXPECT_FALSE(c.is_cancelled());
+    h0.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h1.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h2.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h3.unlock();
+    EXPECT_FALSE(c.is_cancelled());
+    h4.unlock();
+
+    fut0.wait();
+    EXPECT_TRUE(c.is_cancelled());
+    fut1.wait();
+    fut2.wait();
+    fut3.wait();
+    fut4.wait();
+
+    EXPECT_TRUE(c.is_cancelled());
+}
diff --git a/faux-folly/folly/test/ContainerTraitsTest.cpp b/faux-folly/folly/test/ContainerTraitsTest.cpp
new file mode 100644
index 0000000..fccef81
--- /dev/null
+++ b/faux-folly/folly/test/ContainerTraitsTest.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 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/ContainerTraits.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace folly;
+
+struct Node {
+  size_t copies = 0;
+  Node() noexcept {};
+  Node(const Node& n) noexcept { copies = n.copies; ++copies; }
+  Node(Node&& n) noexcept { swap(copies, n.copies); ++copies; }
+};
+
+template <class T>
+class VectorWrapper {
+public:
+  using value_type = T;
+  vector<T>& underlying;
+  explicit VectorWrapper(vector<T>& v) : underlying(v) {}
+  void push_back(const T& t) { underlying.push_back(t); }
+};
+
+TEST(ContainerTraits, WithoutEmplaceBack) {
+  vector<Node> v;
+  VectorWrapper<Node> vw(v);
+  container_emplace_back_or_push_back(vw);
+  EXPECT_EQ(1, v.at(0).copies);
+}
+
+TEST(ContainerTraits, WithEmplaceBack) {
+  vector<Node> v;
+  container_emplace_back_or_push_back(v);
+  EXPECT_EQ(0, v.at(0).copies);
+}
diff --git a/faux-folly/folly/test/ConvTest.cpp b/faux-folly/folly/test/ConvTest.cpp
new file mode 100644
index 0000000..8a99c17
--- /dev/null
+++ b/faux-folly/folly/test/ConvTest.cpp
@@ -0,0 +1,1193 @@
+/*
+ * Copyright 2015 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/Benchmark.h>
+#include <folly/Conv.h>
+#include <folly/Foreach.h>
+#include <boost/lexical_cast.hpp>
+#include <gtest/gtest.h>
+#include <limits>
+#include <stdexcept>
+
+using namespace std;
+using namespace folly;
+
+static int8_t s8;
+static uint8_t u8;
+static int16_t s16;
+static uint16_t u16;
+static int32_t s32;
+static uint32_t u32;
+static int64_t s64;
+static uint64_t u64;
+
+namespace {
+
+/* For some reason, GCC 4.9 doesn't implicitly cast 'const char[]' to
+ * 'const char*' to folly::StringPiece. This cast helps it along.
+ */
+inline const char* to_ccs(const char ra[])
+{
+    return static_cast<const char*>(ra);
+}
+
+} /* anonymous namespace */
+
+TEST(Conv, digits10Minimal) {
+  // Not much of a test (and it's included in the test below anyway).
+  // I just want to inspect the generated assembly for this function.
+  folly::doNotOptimizeAway(digits10(random() * random()));
+}
+
+TEST(Conv, digits10) {
+  char buffer[100];
+  uint64_t power;
+
+  // first, some basic sniff tests
+  EXPECT_EQ( 1, digits10(0));
+  EXPECT_EQ( 1, digits10(1));
+  EXPECT_EQ( 1, digits10(9));
+  EXPECT_EQ( 2, digits10(10));
+  EXPECT_EQ( 2, digits10(99));
+  EXPECT_EQ( 3, digits10(100));
+  EXPECT_EQ( 3, digits10(999));
+  EXPECT_EQ( 4, digits10(1000));
+  EXPECT_EQ( 4, digits10(9999));
+  EXPECT_EQ(20, digits10(18446744073709551615ULL));
+
+  // try the first X nonnegatives.
+  // Covers some more cases of 2^p, 10^p
+  for (uint64_t i = 0; i < 100000; i++) {
+    snprintf(buffer, sizeof(buffer), "%lu", i);
+    EXPECT_EQ(strlen(buffer), digits10(i));
+  }
+
+  // try powers of 2
+  power = 1;
+  for (int p = 0; p < 64; p++) {
+    snprintf(buffer, sizeof(buffer), "%lu", power);
+    EXPECT_EQ(strlen(buffer), digits10(power));
+    snprintf(buffer, sizeof(buffer), "%lu", power - 1);
+    EXPECT_EQ(strlen(buffer), digits10(power - 1));
+    snprintf(buffer, sizeof(buffer), "%lu", power + 1);
+    EXPECT_EQ(strlen(buffer), digits10(power + 1));
+    power *= 2;
+  }
+
+  // try powers of 10
+  power = 1;
+  for (int p = 0; p < 20; p++) {
+    snprintf(buffer, sizeof(buffer), "%lu", power);
+    EXPECT_EQ(strlen(buffer), digits10(power));
+    snprintf(buffer, sizeof(buffer), "%lu", power - 1);
+    EXPECT_EQ(strlen(buffer), digits10(power - 1));
+    snprintf(buffer, sizeof(buffer), "%lu", power + 1);
+    EXPECT_EQ(strlen(buffer), digits10(power + 1));
+    power *= 10;
+  }
+}
+
+// Test to<T>(T)
+TEST(Conv, Type2Type) {
+  int intV = 42;
+  EXPECT_EQ(to<int>(intV), 42);
+
+  float floatV = 4.2;
+  EXPECT_EQ(to<float>(floatV), 4.2f);
+
+  double doubleV = 0.42;
+  EXPECT_EQ(to<double>(doubleV), 0.42);
+
+  std::string stringV = "StdString";
+  EXPECT_EQ(to<std::string>(stringV), "StdString");
+
+  folly::fbstring fbStrV = "FBString";
+  EXPECT_EQ(to<folly::fbstring>(fbStrV), "FBString");
+
+  folly::StringPiece spV("StringPiece");
+  EXPECT_EQ(to<folly::StringPiece>(spV), to_ccs("StringPiece"));
+
+  // Rvalues
+  EXPECT_EQ(to<int>(42), 42);
+  EXPECT_EQ(to<float>(4.2f), 4.2f);
+  EXPECT_EQ(to<double>(.42), .42);
+  EXPECT_EQ(to<std::string>(std::string("Hello")), "Hello");
+  EXPECT_EQ(to<folly::fbstring>(folly::fbstring("hello")), "hello");
+  EXPECT_EQ(to<folly::StringPiece>(folly::StringPiece("Forty Two")),
+            to_ccs("Forty Two"));
+}
+
+TEST(Conv, Integral2Integral) {
+  // Same size, different signs
+  s64 = numeric_limits<uint8_t>::max();
+  EXPECT_EQ(to<uint8_t>(s64), s64);
+
+  s64 = numeric_limits<int8_t>::max();
+  EXPECT_EQ(to<int8_t>(s64), s64);
+}
+
+TEST(Conv, Floating2Floating) {
+  float f1 = 1e3;
+  double d1 = to<double>(f1);
+  EXPECT_EQ(f1, d1);
+
+  double d2 = 23.0;
+  auto f2 = to<float>(d2);
+  EXPECT_EQ(double(f2), d2);
+
+  double invalidFloat = std::numeric_limits<double>::max();
+  EXPECT_ANY_THROW(to<float>(invalidFloat));
+  invalidFloat = -std::numeric_limits<double>::max();
+  EXPECT_ANY_THROW(to<float>(invalidFloat));
+
+  try {
+    auto shouldWork = to<float>(std::numeric_limits<double>::min());
+    // The value of `shouldWork' is an implementation defined choice
+    // between the following two alternatives.
+    EXPECT_TRUE(shouldWork == std::numeric_limits<float>::min() ||
+                shouldWork == 0.f);
+  } catch (...) {
+    EXPECT_TRUE(false);
+  }
+}
+
+template <class String>
+void testIntegral2String() {
+}
+
+template <class String, class Int, class... Ints>
+void testIntegral2String() {
+  typedef typename make_unsigned<Int>::type Uint;
+  typedef typename make_signed<Int>::type Sint;
+
+  Uint value = 123;
+  EXPECT_EQ(to<String>(value), "123");
+  Sint svalue = 123;
+  EXPECT_EQ(to<String>(svalue), "123");
+  svalue = -123;
+  EXPECT_EQ(to<String>(svalue), "-123");
+
+  value = numeric_limits<Uint>::min();
+  EXPECT_EQ(to<Uint>(to<String>(value)), value);
+  value = numeric_limits<Uint>::max();
+  EXPECT_EQ(to<Uint>(to<String>(value)), value);
+
+  svalue = numeric_limits<Sint>::min();
+  EXPECT_EQ(to<Sint>(to<String>(svalue)), svalue);
+  value = numeric_limits<Sint>::max();
+  EXPECT_EQ(to<Sint>(to<String>(svalue)), svalue);
+
+  testIntegral2String<String, Ints...>();
+}
+
+#if FOLLY_HAVE_INT128_T
+template <class String>
+void test128Bit2String() {
+  typedef unsigned __int128 Uint;
+  typedef __int128 Sint;
+
+  EXPECT_EQ(detail::digitsEnough<unsigned __int128>(), 39);
+
+  Uint value = 123;
+  EXPECT_EQ(to<String>(value), "123");
+  Sint svalue = 123;
+  EXPECT_EQ(to<String>(svalue), "123");
+  svalue = -123;
+  EXPECT_EQ(to<String>(svalue), "-123");
+
+  value = __int128(1) << 64;
+  EXPECT_EQ(to<String>(value), "18446744073709551616");
+
+  svalue =  -(__int128(1) << 64);
+  EXPECT_EQ(to<String>(svalue), "-18446744073709551616");
+
+  value = 0;
+  EXPECT_EQ(to<String>(value), "0");
+
+  svalue = 0;
+  EXPECT_EQ(to<String>(svalue), "0");
+
+  // TODO: the following do not compile to<__int128> ...
+
+#if 0
+  value = numeric_limits<Uint>::min();
+  EXPECT_EQ(to<Uint>(to<String>(value)), value);
+  value = numeric_limits<Uint>::max();
+  EXPECT_EQ(to<Uint>(to<String>(value)), value);
+
+  svalue = numeric_limits<Sint>::min();
+  EXPECT_EQ(to<Sint>(to<String>(svalue)), svalue);
+  value = numeric_limits<Sint>::max();
+  EXPECT_EQ(to<Sint>(to<String>(svalue)), svalue);
+#endif
+}
+
+#endif
+
+TEST(Conv, Integral2String) {
+  testIntegral2String<std::string, char, short, int, long>();
+  testIntegral2String<fbstring, char, short, int, long>();
+
+#if FOLLY_HAVE_INT128_T
+  test128Bit2String<std::string>();
+  test128Bit2String<fbstring>();
+#endif
+}
+
+template <class String>
+void testString2Integral() {
+}
+
+template <class String, class Int, class... Ints>
+void testString2Integral() {
+  typedef typename make_unsigned<Int>::type Uint;
+  typedef typename make_signed<Int>::type Sint;
+
+  // Unsigned numbers small enough to fit in a signed type
+  static const String strings[] = {
+    "0",
+    "00",
+    "2 ",
+    " 84",
+    " \n 123    \t\n",
+    " 127",
+    "0000000000000000000000000042"
+  };
+  static const Uint values[] = {
+    0,
+    0,
+    2,
+    84,
+    123,
+    127,
+    42
+  };
+  FOR_EACH_RANGE (i, 0, sizeof(strings) / sizeof(*strings)) {
+    EXPECT_EQ(to<Uint>(strings[i]), values[i]);
+    EXPECT_EQ(to<Sint>(strings[i]), values[i]);
+  }
+
+  // Unsigned numbers that won't fit in the signed variation
+  static const String uStrings[] = {
+    " 128",
+    "213",
+    "255"
+  };
+  static const Uint uValues[] = {
+    128,
+    213,
+    255
+  };
+  FOR_EACH_RANGE (i, 0, sizeof(uStrings)/sizeof(*uStrings)) {
+    EXPECT_EQ(to<Uint>(uStrings[i]), uValues[i]);
+    if (sizeof(Int) == 1) {
+      EXPECT_THROW(to<Sint>(uStrings[i]), std::range_error);
+    }
+  }
+
+  if (sizeof(Int) >= 4) {
+    static const String strings2[] = {
+      "256",
+      "6324 ",
+      "63245675 ",
+      "2147483647"
+    };
+    static const Uint values2[] = {
+      (Uint)256,
+      (Uint)6324,
+      (Uint)63245675,
+      (Uint)2147483647
+    };
+    FOR_EACH_RANGE (i, 0, sizeof(strings2)/sizeof(*strings2)) {
+      EXPECT_EQ(to<Uint>(strings2[i]), values2[i]);
+      EXPECT_EQ(to<Sint>(strings2[i]), values2[i]);
+    }
+
+    static const String uStrings2[] = {
+      "2147483648",
+      "3147483648",
+      "4147483648",
+      "4000000000",
+    };
+    static const Uint uValues2[] = {
+      (Uint)2147483648U,
+      (Uint)3147483648U,
+      (Uint)4147483648U,
+      (Uint)4000000000U,
+    };
+    FOR_EACH_RANGE (i, 0, sizeof(uStrings2)/sizeof(uStrings2)) {
+      EXPECT_EQ(to<Uint>(uStrings2[i]), uValues2[i]);
+      if (sizeof(Int) == 4) {
+        EXPECT_THROW(to<Sint>(uStrings2[i]), std::range_error);
+      }
+    }
+  }
+
+  if (sizeof(Int) >= 8) {
+    static_assert(sizeof(Int) <= 8, "Now that would be interesting");
+    static const String strings3[] = {
+      "2147483648",
+      "5000000001",
+      "25687346509278435",
+      "100000000000000000",
+      "9223372036854775807",
+    };
+    static const Uint values3[] = {
+      (Uint)2147483648ULL,
+      (Uint)5000000001ULL,
+      (Uint)25687346509278435ULL,
+      (Uint)100000000000000000ULL,
+      (Uint)9223372036854775807ULL,
+    };
+    FOR_EACH_RANGE (i, 0, sizeof(strings3)/sizeof(*strings3)) {
+      EXPECT_EQ(to<Uint>(strings3[i]), values3[i]);
+      EXPECT_EQ(to<Sint>(strings3[i]), values3[i]);
+    }
+
+    static const String uStrings3[] = {
+      "9223372036854775808",
+      "9987435987394857987",
+      "17873648761234698740",
+      "18446744073709551615",
+    };
+    static const Uint uValues3[] = {
+      (Uint)9223372036854775808ULL,
+      (Uint)9987435987394857987ULL,
+      (Uint)17873648761234698740ULL,
+      (Uint)18446744073709551615ULL,
+    };
+    FOR_EACH_RANGE (i, 0, sizeof(uStrings3)/sizeof(*uStrings3)) {
+      EXPECT_EQ(to<Uint>(uStrings3[i]), uValues3[i]);
+      if (sizeof(Int) == 8) {
+        EXPECT_THROW(to<Sint>(uStrings3[i]), std::range_error);
+      }
+    }
+  }
+
+  // Minimum possible negative values, and negative sign overflow
+  static const String strings4[] = {
+    "-128",
+    "-32768",
+    "-2147483648",
+    "-9223372036854775808",
+  };
+  static const String strings5[] = {
+    "-129",
+    "-32769",
+    "-2147483649",
+    "-9223372036854775809",
+  };
+  static const Sint values4[] = {
+    (Sint)-128LL,
+    (Sint)-32768LL,
+    (Sint)-2147483648LL,
+    (Sint)(-9223372036854775807LL - 1),
+  };
+  FOR_EACH_RANGE (i, 0, sizeof(strings4)/sizeof(*strings4)) {
+    if (sizeof(Int) > std::pow(2, i)) {
+      EXPECT_EQ(values4[i], to<Sint>(strings4[i]));
+      EXPECT_EQ(values4[i] - 1, to<Sint>(strings5[i]));
+    } else if (sizeof(Int) == std::pow(2, i)) {
+      EXPECT_EQ(values4[i], to<Sint>(strings4[i]));
+      EXPECT_THROW(to<Sint>(strings5[i]), std::range_error);
+    } else {
+      EXPECT_THROW(to<Sint>(strings4[i]), std::range_error);
+      EXPECT_THROW(to<Sint>(strings5[i]), std::range_error);
+    }
+  }
+
+  // Bogus string values
+  static const String bogusStrings[] = {
+    "",
+    "0x1234",
+    "123L",
+    "123a",
+    "x 123 ",
+    "234 y",
+    "- 42",  // whitespace is not allowed between the sign and the value
+    " +   13 ",
+    "12345678901234567890123456789",
+  };
+  for (const auto& str : bogusStrings) {
+    EXPECT_THROW(to<Sint>(str), std::range_error);
+    EXPECT_THROW(to<Uint>(str), std::range_error);
+  }
+
+  // A leading '+' character is only allowed when converting to signed types.
+  String posSign("+42");
+  EXPECT_EQ(42, to<Sint>(posSign));
+  EXPECT_THROW(to<Uint>(posSign), std::range_error);
+
+  testString2Integral<String, Ints...>();
+}
+
+TEST(Conv, String2Integral) {
+  testString2Integral<const char*, signed char, short, int, long, long long>();
+  testString2Integral<std::string, signed char, short, int, long, long long>();
+  testString2Integral<fbstring, signed char, short, int, long, long long>();
+
+  // Testing the behavior of the StringPiece* API
+  // StringPiece* normally parses as much valid data as it can,
+  // and advances the StringPiece to the end of the valid data.
+  char buf1[] = "100foo";
+  StringPiece sp1(buf1);
+  EXPECT_EQ(100, to<uint8_t>(&sp1));
+  EXPECT_EQ(buf1 + 3, sp1.begin());
+  // However, if the next character would cause an overflow it throws a
+  // range_error rather than consuming only as much as it can without
+  // overflowing.
+  char buf2[] = "1002";
+  StringPiece sp2(buf2);
+  EXPECT_THROW(to<uint8_t>(&sp2), std::range_error);
+  EXPECT_EQ(buf2, sp2.begin());
+}
+
+TEST(Conv, StringPiece2Integral) {
+  string s = "  +123  hello world  ";
+  StringPiece sp = s;
+  EXPECT_EQ(to<int>(&sp), 123);
+  EXPECT_EQ(sp, to_ccs("  hello world  "));
+}
+
+TEST(Conv, StringPieceAppend) {
+  string s = "foobar";
+  {
+    StringPiece sp(s, 0, 3);
+    string result = to<string>(s, sp);
+    EXPECT_EQ(result, "foobarfoo");
+  }
+  {
+    StringPiece sp1(s, 0, 3);
+    StringPiece sp2(s, 3, 3);
+    string result = to<string>(sp1, sp2);
+    EXPECT_EQ(result, s);
+  }
+}
+
+TEST(Conv, BadStringToIntegral) {
+  // Note that leading spaces (e.g.  " 1") are valid.
+  vector<string> v = { "a", "", " ", "\n", " a0", "abcdef", "1Z", "!#" };
+  for (auto& s: v) {
+    EXPECT_THROW(to<int>(s), std::range_error) << "s=" << s;
+  }
+}
+
+template <class String>
+void testIdenticalTo() {
+  String s("Yukkuri shiteitte ne!!!");
+
+  String result = to<String>(s);
+  EXPECT_EQ(result, s);
+}
+
+template <class String>
+void testVariadicTo() {
+  String s;
+  toAppend(&s);
+  toAppend("Lorem ipsum ", 1234, String(" dolor amet "), 567.89, '!', &s);
+  EXPECT_EQ(s, "Lorem ipsum 1234 dolor amet 567.89!");
+
+  s = to<String>();
+  EXPECT_TRUE(s.empty());
+
+  s = to<String>("Lorem ipsum ", nullptr, 1234, " dolor amet ", 567.89, '.');
+  EXPECT_EQ(s, "Lorem ipsum 1234 dolor amet 567.89.");
+}
+
+template <class String>
+void testIdenticalToDelim() {
+  String s("Yukkuri shiteitte ne!!!");
+
+  String charDelim = toDelim<String>('$', s);
+  EXPECT_EQ(charDelim, s);
+
+  String strDelim = toDelim<String>(String(">_<"), s);
+  EXPECT_EQ(strDelim, s);
+}
+
+template <class String>
+void testVariadicToDelim() {
+  String s;
+  toAppendDelim(":", &s);
+  toAppendDelim(
+      ":", "Lorem ipsum ", 1234, String(" dolor amet "), 567.89, '!', &s);
+  EXPECT_EQ(s, "Lorem ipsum :1234: dolor amet :567.89:!");
+
+  s = toDelim<String>(':');
+  EXPECT_TRUE(s.empty());
+
+  s = toDelim<String>(
+      ":", "Lorem ipsum ", nullptr, 1234, " dolor amet ", 567.89, '.');
+  EXPECT_EQ(s, "Lorem ipsum ::1234: dolor amet :567.89:.");
+}
+
+TEST(Conv, NullString) {
+  string s1 = to<string>((char *) nullptr);
+  EXPECT_TRUE(s1.empty());
+  fbstring s2 = to<fbstring>((char *) nullptr);
+  EXPECT_TRUE(s2.empty());
+}
+
+TEST(Conv, VariadicTo) {
+  testIdenticalTo<string>();
+  testIdenticalTo<fbstring>();
+  testVariadicTo<string>();
+  testVariadicTo<fbstring>();
+}
+
+TEST(Conv, VariadicToDelim) {
+  testIdenticalToDelim<string>();
+  testIdenticalToDelim<fbstring>();
+  testVariadicToDelim<string>();
+  testVariadicToDelim<fbstring>();
+}
+
+template <class String>
+void testDoubleToString() {
+  EXPECT_EQ(to<string>(0.0), "0");
+  EXPECT_EQ(to<string>(0.5), "0.5");
+  EXPECT_EQ(to<string>(10.25), "10.25");
+  EXPECT_EQ(to<string>(1.123e10), "11230000000");
+}
+
+TEST(Conv, DoubleToString) {
+  testDoubleToString<string>();
+  testDoubleToString<fbstring>();
+}
+
+TEST(Conv, FBStringToString) {
+  fbstring foo("foo");
+  string ret = to<string>(foo);
+  EXPECT_EQ(ret, "foo");
+  string ret2 = to<string>(foo, 2);
+  EXPECT_EQ(ret2, "foo2");
+}
+
+TEST(Conv, StringPieceToDouble) {
+  string s = "2134123.125 zorro";
+  StringPiece pc(s);
+  EXPECT_EQ(to<double>(&pc), 2134123.125);
+  EXPECT_EQ(pc, to_ccs(" zorro"));
+
+  EXPECT_THROW(to<double>(StringPiece(s)), std::range_error);
+  EXPECT_EQ(to<double>(StringPiece(s.data(), pc.data())), 2134123.125);
+
+// Test NaN conversion
+  try {
+    to<double>("not a number");
+    EXPECT_TRUE(false);
+  } catch (const std::range_error &) {
+  }
+
+  EXPECT_TRUE(std::isnan(to<double>("NaN")));
+  EXPECT_EQ(to<double>("inf"), numeric_limits<double>::infinity());
+  EXPECT_EQ(to<double>("infinity"), numeric_limits<double>::infinity());
+  EXPECT_THROW(to<double>("infinitX"), std::range_error);
+  EXPECT_EQ(to<double>("-inf"), -numeric_limits<double>::infinity());
+  EXPECT_EQ(to<double>("-infinity"), -numeric_limits<double>::infinity());
+  EXPECT_THROW(to<double>("-infinitX"), std::range_error);
+}
+
+TEST(Conv, EmptyStringToInt) {
+  string s = "";
+  StringPiece pc(s);
+
+  try {
+    to<int>(pc);
+    EXPECT_TRUE(false);
+  } catch (const std::range_error &) {
+  }
+}
+
+TEST(Conv, CorruptedStringToInt) {
+  string s = "-1";
+  StringPiece pc(s.data(), s.data() + 1); // Only  "-"
+
+  try {
+    to<int64_t>(&pc);
+    EXPECT_TRUE(false);
+  } catch (const std::range_error &) {
+  }
+}
+
+TEST(Conv, EmptyStringToDouble) {
+  string s = "";
+  StringPiece pc(s);
+
+  try {
+    to<double>(pc);
+    EXPECT_TRUE(false);
+  } catch (const std::range_error &) {
+  }
+}
+
+TEST(Conv, IntToDouble) {
+  auto d = to<double>(42);
+  EXPECT_EQ(d, 42);
+  /* This seems not work in ubuntu11.10, gcc 4.6.1
+  try {
+    auto f = to<float>(957837589847);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+    //LOG(INFO) << e.what();
+  }
+  */
+}
+
+TEST(Conv, DoubleToInt) {
+  auto i = to<int>(42.0);
+  EXPECT_EQ(i, 42);
+  try {
+    auto i = to<int>(42.1);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+    //LOG(INFO) << e.what();
+  }
+}
+
+TEST(Conv, EnumToInt) {
+  enum A { x = 42, y = 420, z = 65 };
+  auto i = to<int>(x);
+  EXPECT_EQ(i, 42);
+  auto j = to<char>(x);
+  EXPECT_EQ(j, 42);
+  try {
+    auto i = to<char>(y);
+    LOG(ERROR) << static_cast<unsigned int>(i);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+    //LOG(INFO) << e.what();
+  }
+}
+
+TEST(Conv, EnumToString) {
+  // task 813959
+  enum A { x = 4, y = 420, z = 65 };
+  EXPECT_EQ("foo.4", to<string>("foo.", x));
+  EXPECT_EQ("foo.420", to<string>("foo.", y));
+  EXPECT_EQ("foo.65", to<string>("foo.", z));
+}
+
+TEST(Conv, IntToEnum) {
+  enum A { x = 42, y = 420 };
+  auto i = to<A>(42);
+  EXPECT_EQ(i, x);
+  auto j = to<A>(100);
+  EXPECT_EQ(j, 100);
+  try {
+    auto i = to<A>(5000000000L);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+    //LOG(INFO) << e.what();
+  }
+}
+
+TEST(Conv, UnsignedEnum) {
+  enum E : uint32_t { x = 3000000000U };
+  auto u = to<uint32_t>(x);
+  EXPECT_EQ(u, 3000000000U);
+  auto s = to<string>(x);
+  EXPECT_EQ("3000000000", s);
+  auto e = to<E>(3000000000U);
+  EXPECT_EQ(e, x);
+  try {
+    auto i = to<int32_t>(x);
+    LOG(ERROR) << to<uint32_t>(x);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+  }
+}
+
+#if defined(__clang__) || __GNUC_PREREQ(4, 7)
+// to<enum class> and to(enum class) only supported in gcc 4.7 onwards
+
+TEST(Conv, UnsignedEnumClass) {
+  enum class E : uint32_t { x = 3000000000U };
+  auto u = to<uint32_t>(E::x);
+  EXPECT_GT(u, 0);
+  EXPECT_EQ(u, 3000000000U);
+  auto s = to<string>(E::x);
+  EXPECT_EQ("3000000000", s);
+  auto e = to<E>(3000000000U);
+  EXPECT_EQ(e, E::x);
+  try {
+    auto i = to<int32_t>(E::x);
+    LOG(ERROR) << to<uint32_t>(E::x);
+    EXPECT_TRUE(false);
+  } catch (std::range_error& e) {
+  }
+}
+
+// Multi-argument to<string> uses toAppend, a different code path than
+// to<string>(enum).
+TEST(Conv, EnumClassToString) {
+  enum class A { x = 4, y = 420, z = 65 };
+  EXPECT_EQ("foo.4", to<string>("foo.", A::x));
+  EXPECT_EQ("foo.420", to<string>("foo.", A::y));
+  EXPECT_EQ("foo.65", to<string>("foo.", A::z));
+}
+
+#endif // gcc 4.7 onwards
+
+template<typename Src>
+void testStr2Bool() {
+  EXPECT_FALSE(to<bool>(Src("0")));
+  EXPECT_FALSE(to<bool>(Src("  000  ")));
+
+  EXPECT_FALSE(to<bool>(Src("n")));
+  EXPECT_FALSE(to<bool>(Src("no")));
+  EXPECT_FALSE(to<bool>(Src("false")));
+  EXPECT_FALSE(to<bool>(Src("False")));
+  EXPECT_FALSE(to<bool>(Src("  fAlSe"  )));
+  EXPECT_FALSE(to<bool>(Src("F")));
+  EXPECT_FALSE(to<bool>(Src("off")));
+
+  EXPECT_TRUE(to<bool>(Src("1")));
+  EXPECT_TRUE(to<bool>(Src("  001 ")));
+  EXPECT_TRUE(to<bool>(Src("y")));
+  EXPECT_TRUE(to<bool>(Src("yes")));
+  EXPECT_TRUE(to<bool>(Src("\nyEs\t")));
+  EXPECT_TRUE(to<bool>(Src("true")));
+  EXPECT_TRUE(to<bool>(Src("True")));
+  EXPECT_TRUE(to<bool>(Src("T")));
+  EXPECT_TRUE(to<bool>(Src("on")));
+
+  EXPECT_THROW(to<bool>(Src("")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("2")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("11")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("19")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("o")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("fal")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("tru")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("ye")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("yes foo")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("bar no")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("one")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("true_")), std::range_error);
+  EXPECT_THROW(to<bool>(Src("bogus_token_that_is_too_long")),
+               std::range_error);
+}
+
+TEST(Conv, StringToBool) {
+  // testStr2Bool<const char *>();
+  testStr2Bool<std::string>();
+
+  // Test with strings that are not NUL terminated.
+  const char buf[] = "01234";
+  EXPECT_FALSE(to<bool>(StringPiece(buf, buf + 1)));  // "0"
+  EXPECT_TRUE(to<bool>(StringPiece(buf + 1, buf + 2)));  // "1"
+  const char buf2[] = "one two three";
+  EXPECT_TRUE(to<bool>(StringPiece(buf2, buf2 + 2)));  // "on"
+  const char buf3[] = "false";
+  EXPECT_THROW(to<bool>(StringPiece(buf3, buf3 + 3)),  // "fal"
+               std::range_error);
+
+  // Test the StringPiece* API
+  const char buf4[] = "001foo";
+  StringPiece sp4(buf4);
+  EXPECT_TRUE(to<bool>(&sp4));
+  EXPECT_EQ(buf4 + 3, sp4.begin());
+  const char buf5[] = "0012";
+  StringPiece sp5(buf5);
+  EXPECT_THROW(to<bool>(&sp5), std::range_error);
+  EXPECT_EQ(buf5, sp5.begin());
+}
+
+TEST(Conv, NewUint64ToString) {
+  char buf[21];
+
+#define THE_GREAT_EXPECTATIONS(n, len)                  \
+  do {                                                  \
+    EXPECT_EQ((len), uint64ToBufferUnsafe((n), buf));   \
+    buf[(len)] = 0;                                     \
+    auto s = string(#n);                                \
+    s = s.substr(0, s.size() - 2);                      \
+    EXPECT_EQ(s, buf);                                  \
+  } while (0)
+
+  THE_GREAT_EXPECTATIONS(0UL, 1);
+  THE_GREAT_EXPECTATIONS(1UL, 1);
+  THE_GREAT_EXPECTATIONS(12UL, 2);
+  THE_GREAT_EXPECTATIONS(123UL, 3);
+  THE_GREAT_EXPECTATIONS(1234UL, 4);
+  THE_GREAT_EXPECTATIONS(12345UL, 5);
+  THE_GREAT_EXPECTATIONS(123456UL, 6);
+  THE_GREAT_EXPECTATIONS(1234567UL, 7);
+  THE_GREAT_EXPECTATIONS(12345678UL, 8);
+  THE_GREAT_EXPECTATIONS(123456789UL, 9);
+  THE_GREAT_EXPECTATIONS(1234567890UL, 10);
+  THE_GREAT_EXPECTATIONS(12345678901UL, 11);
+  THE_GREAT_EXPECTATIONS(123456789012UL, 12);
+  THE_GREAT_EXPECTATIONS(1234567890123UL, 13);
+  THE_GREAT_EXPECTATIONS(12345678901234UL, 14);
+  THE_GREAT_EXPECTATIONS(123456789012345UL, 15);
+  THE_GREAT_EXPECTATIONS(1234567890123456UL, 16);
+  THE_GREAT_EXPECTATIONS(12345678901234567UL, 17);
+  THE_GREAT_EXPECTATIONS(123456789012345678UL, 18);
+  THE_GREAT_EXPECTATIONS(1234567890123456789UL, 19);
+  THE_GREAT_EXPECTATIONS(18446744073709551614UL, 20);
+  THE_GREAT_EXPECTATIONS(18446744073709551615UL, 20);
+
+#undef THE_GREAT_EXPECTATIONS
+}
+
+TEST(Conv, allocate_size) {
+  std::string str1 = "meh meh meh";
+  std::string str2 = "zdech zdech zdech";
+
+  auto res1 = folly::to<std::string>(str1, ".", str2);
+  EXPECT_EQ(res1, str1 + "." + str2);
+
+  std::string res2; //empty
+  toAppendFit(str1, str2, 1, &res2);
+  EXPECT_EQ(res2, str1 + str2 + "1");
+
+  std::string res3;
+  toAppendDelimFit(",", str1, str2, &res3);
+  EXPECT_EQ(res3, str1 + "," + str2);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Benchmarks for ASCII to int conversion
+////////////////////////////////////////////////////////////////////////////////
+// @author: Rajat Goel (rajat)
+
+static int64_t handwrittenAtoi(const char* start, const char* end) {
+
+  bool positive = true;
+  int64_t retVal = 0;
+
+  if (start == end) {
+    throw std::runtime_error("empty string");
+  }
+
+  while (start < end && isspace(*start)) {
+    ++start;
+  }
+
+  switch (*start) {
+    case '-':
+      positive = false;
+    case '+':
+      ++start;
+    default:;
+  }
+
+  while (start < end && *start >= '0' && *start <= '9') {
+    auto const newRetVal = retVal * 10 + (*start++ - '0');
+    if (newRetVal < retVal) {
+      throw std::runtime_error("overflow");
+    }
+    retVal = newRetVal;
+  }
+
+  if (start != end) {
+    throw std::runtime_error("extra chars at the end");
+  }
+
+  return positive ? retVal : -retVal;
+}
+
+static StringPiece pc1 = "1234567890123456789";
+
+void handwrittenAtoiMeasure(unsigned int n, unsigned int digits) {
+  auto p = pc1.subpiece(pc1.size() - digits, digits);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(handwrittenAtoi(p.begin(), p.end()));
+  }
+}
+
+void follyAtoiMeasure(unsigned int n, unsigned int digits) {
+  auto p = pc1.subpiece(pc1.size() - digits, digits);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(folly::to<int64_t>(p.begin(), p.end()));
+  }
+}
+
+void clibAtoiMeasure(unsigned int n, unsigned int digits) {
+  auto p = pc1.subpiece(pc1.size() - digits, digits);
+  assert(*p.end() == 0);
+  static_assert((sizeof(long) == 8) || (sizeof(long long) == 8), "Can't find 64-bit native type");
+  if (sizeof(long) == 8) {
+      FOR_EACH_RANGE (i, 0, n) {
+          doNotOptimizeAway(atol(p.begin()));
+      }
+  } else if (sizeof(long long) == 8) {
+      FOR_EACH_RANGE (i, 0, n) {
+          doNotOptimizeAway(atoll(p.begin()));
+      }
+  }
+}
+
+void clibStrtoulMeasure(unsigned int n, unsigned int digits) {
+  auto p = pc1.subpiece(pc1.size() - digits, digits);
+  assert(*p.end() == 0);
+  char * endptr;
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(strtoul(p.begin(), &endptr, 10));
+  }
+}
+
+void lexicalCastMeasure(unsigned int n, unsigned int digits) {
+  auto p = pc1.subpiece(pc1.size() - digits, digits);
+  assert(*p.end() == 0);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(boost::lexical_cast<uint64_t>(p.begin()));
+  }
+}
+
+// Benchmarks for unsigned to string conversion, raw
+
+unsigned u64ToAsciiTable(uint64_t value, char* dst) {
+  static const char digits[201] =
+    "00010203040506070809"
+    "10111213141516171819"
+    "20212223242526272829"
+    "30313233343536373839"
+    "40414243444546474849"
+    "50515253545556575859"
+    "60616263646566676869"
+    "70717273747576777879"
+    "80818283848586878889"
+    "90919293949596979899";
+
+  uint32_t const length = digits10(value);
+  uint32_t next = length - 1;
+  while (value >= 100) {
+    auto const i = (value % 100) * 2;
+    value /= 100;
+    dst[next] = digits[i + 1];
+    dst[next - 1] = digits[i];
+    next -= 2;
+  }
+  // Handle last 1-2 digits
+  if (value < 10) {
+    dst[next] = '0' + uint32_t(value);
+  } else {
+    auto i = uint32_t(value) * 2;
+    dst[next] = digits[i + 1];
+    dst[next - 1] = digits[i];
+  }
+  return length;
+}
+
+void u64ToAsciiTableBM(unsigned int n, uint64_t value) {
+  // This is too fast, need to do 10 times per iteration
+  char buf[20];
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(u64ToAsciiTable(value + n, buf));
+  }
+}
+
+unsigned u64ToAsciiClassic(uint64_t value, char* dst) {
+  // Write backwards.
+  char* next = (char*)dst;
+  char* start = next;
+  do {
+    *next++ = '0' + (value % 10);
+    value /= 10;
+  } while (value != 0);
+  unsigned length = next - start;
+
+  // Reverse in-place.
+  next--;
+  while (next > start) {
+    char swap = *next;
+    *next = *start;
+    *start = swap;
+    next--;
+    start++;
+  }
+  return length;
+}
+
+void u64ToAsciiClassicBM(unsigned int n, uint64_t value) {
+  // This is too fast, need to do 10 times per iteration
+  char buf[20];
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(u64ToAsciiClassic(value + n, buf));
+  }
+}
+
+void u64ToAsciiFollyBM(unsigned int n, uint64_t value) {
+  // This is too fast, need to do 10 times per iteration
+  char buf[20];
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(uint64ToBufferUnsafe(value + n, buf));
+  }
+}
+
+// Benchmark uitoa with string append
+
+void u2aAppendClassicBM(unsigned int n, uint64_t value) {
+  string s;
+  FOR_EACH_RANGE (i, 0, n) {
+    // auto buf = &s.back() + 1;
+    char buffer[20];
+    s.append(buffer, u64ToAsciiClassic(value, buffer));
+    doNotOptimizeAway(s.size());
+  }
+}
+
+void u2aAppendFollyBM(unsigned int n, uint64_t value) {
+  string s;
+  FOR_EACH_RANGE (i, 0, n) {
+    // auto buf = &s.back() + 1;
+    char buffer[20];
+    s.append(buffer, uint64ToBufferUnsafe(value, buffer));
+    doNotOptimizeAway(s.size());
+  }
+}
+
+template <class String>
+struct StringIdenticalToBM {
+  StringIdenticalToBM() {}
+  void operator()(unsigned int n, size_t len) const {
+    String s;
+    BENCHMARK_SUSPEND { s.append(len, '0'); }
+    FOR_EACH_RANGE (i, 0, n) {
+      String result = to<String>(s);
+      doNotOptimizeAway(result.size());
+    }
+  }
+};
+
+template <class String>
+struct StringVariadicToBM {
+  StringVariadicToBM() {}
+  void operator()(unsigned int n, size_t len) const {
+    String s;
+    BENCHMARK_SUSPEND { s.append(len, '0'); }
+    FOR_EACH_RANGE (i, 0, n) {
+      String result = to<String>(s, nullptr);
+      doNotOptimizeAway(result.size());
+    }
+  }
+};
+
+static size_t bigInt = 11424545345345;
+static size_t smallInt = 104;
+static char someString[] = "this is some nice string";
+static char otherString[] = "this is a long string, so it's not so nice";
+static char reallyShort[] = "meh";
+static std::string stdString = "std::strings are very nice";
+static float fValue = 1.2355;
+static double dValue = 345345345.435;
+
+BENCHMARK(preallocateTestNoFloat, n) {
+  for (size_t i = 0; i < n; ++i) {
+    auto val1 = to<std::string>(bigInt, someString, stdString, otherString);
+    auto val3 = to<std::string>(reallyShort, smallInt);
+    auto val2 = to<std::string>(bigInt, stdString);
+    auto val4 = to<std::string>(bigInt, stdString, dValue, otherString);
+    auto val5 = to<std::string>(bigInt, someString, reallyShort);
+  }
+}
+
+BENCHMARK(preallocateTestFloat, n) {
+  for (size_t i = 0; i < n; ++i) {
+    auto val1 = to<std::string>(stdString, ',', fValue, dValue);
+    auto val2 = to<std::string>(stdString, ',', dValue);
+  }
+}
+BENCHMARK_DRAW_LINE();
+
+static const StringIdenticalToBM<std::string> stringIdenticalToBM;
+static const StringVariadicToBM<std::string> stringVariadicToBM;
+static const StringIdenticalToBM<fbstring> fbstringIdenticalToBM;
+static const StringVariadicToBM<fbstring> fbstringVariadicToBM;
+
+#define DEFINE_BENCHMARK_GROUP(n)                       \
+  BENCHMARK_PARAM(u64ToAsciiClassicBM, n);              \
+  BENCHMARK_RELATIVE_PARAM(u64ToAsciiTableBM, n);       \
+  BENCHMARK_RELATIVE_PARAM(u64ToAsciiFollyBM, n);       \
+  BENCHMARK_DRAW_LINE();
+
+DEFINE_BENCHMARK_GROUP(1);
+DEFINE_BENCHMARK_GROUP(12);
+DEFINE_BENCHMARK_GROUP(123);
+DEFINE_BENCHMARK_GROUP(1234);
+DEFINE_BENCHMARK_GROUP(12345);
+DEFINE_BENCHMARK_GROUP(123456);
+DEFINE_BENCHMARK_GROUP(1234567);
+DEFINE_BENCHMARK_GROUP(12345678);
+DEFINE_BENCHMARK_GROUP(123456789);
+DEFINE_BENCHMARK_GROUP(1234567890);
+DEFINE_BENCHMARK_GROUP(12345678901);
+DEFINE_BENCHMARK_GROUP(123456789012);
+DEFINE_BENCHMARK_GROUP(1234567890123);
+DEFINE_BENCHMARK_GROUP(12345678901234);
+DEFINE_BENCHMARK_GROUP(123456789012345);
+DEFINE_BENCHMARK_GROUP(1234567890123456);
+DEFINE_BENCHMARK_GROUP(12345678901234567);
+DEFINE_BENCHMARK_GROUP(123456789012345678);
+DEFINE_BENCHMARK_GROUP(1234567890123456789);
+DEFINE_BENCHMARK_GROUP(12345678901234567890U);
+
+#undef DEFINE_BENCHMARK_GROUP
+
+#define DEFINE_BENCHMARK_GROUP(n)                       \
+  BENCHMARK_PARAM(clibAtoiMeasure, n);                  \
+  BENCHMARK_RELATIVE_PARAM(lexicalCastMeasure, n);      \
+  BENCHMARK_RELATIVE_PARAM(handwrittenAtoiMeasure, n);  \
+  BENCHMARK_RELATIVE_PARAM(follyAtoiMeasure, n);        \
+  BENCHMARK_DRAW_LINE();
+
+DEFINE_BENCHMARK_GROUP(1);
+DEFINE_BENCHMARK_GROUP(2);
+DEFINE_BENCHMARK_GROUP(3);
+DEFINE_BENCHMARK_GROUP(4);
+DEFINE_BENCHMARK_GROUP(5);
+DEFINE_BENCHMARK_GROUP(6);
+DEFINE_BENCHMARK_GROUP(7);
+DEFINE_BENCHMARK_GROUP(8);
+DEFINE_BENCHMARK_GROUP(9);
+DEFINE_BENCHMARK_GROUP(10);
+DEFINE_BENCHMARK_GROUP(11);
+DEFINE_BENCHMARK_GROUP(12);
+DEFINE_BENCHMARK_GROUP(13);
+DEFINE_BENCHMARK_GROUP(14);
+DEFINE_BENCHMARK_GROUP(15);
+DEFINE_BENCHMARK_GROUP(16);
+DEFINE_BENCHMARK_GROUP(17);
+DEFINE_BENCHMARK_GROUP(18);
+DEFINE_BENCHMARK_GROUP(19);
+
+#undef DEFINE_BENCHMARK_GROUP
+
+#define DEFINE_BENCHMARK_GROUP(T, n)                    \
+  BENCHMARK_PARAM(T ## VariadicToBM, n);                \
+  BENCHMARK_RELATIVE_PARAM(T ## IdenticalToBM, n);      \
+  BENCHMARK_DRAW_LINE();
+
+DEFINE_BENCHMARK_GROUP(string, 32);
+DEFINE_BENCHMARK_GROUP(string, 1024);
+DEFINE_BENCHMARK_GROUP(string, 32768);
+DEFINE_BENCHMARK_GROUP(fbstring, 32);
+DEFINE_BENCHMARK_GROUP(fbstring, 1024);
+DEFINE_BENCHMARK_GROUP(fbstring, 32768);
+
+#undef DEFINE_BENCHMARK_GROUP
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  auto ret = RUN_ALL_TESTS();
+  if (!ret && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return ret;
+}
diff --git a/faux-folly/folly/test/DemangleTest.cpp b/faux-folly/folly/test/DemangleTest.cpp
new file mode 100644
index 0000000..eab9cff
--- /dev/null
+++ b/faux-folly/folly/test/DemangleTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 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/Demangle.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using folly::demangle;
+
+namespace folly_test {
+struct ThisIsAVeryLongStructureName {
+};
+}  // namespace folly_test
+
+#if FOLLY_HAVE_CPLUS_DEMANGLE_V3_CALLBACK
+TEST(Demangle, demangle) {
+  char expected[] = "folly_test::ThisIsAVeryLongStructureName";
+  EXPECT_STREQ(
+      expected,
+      demangle(typeid(folly_test::ThisIsAVeryLongStructureName)).c_str());
+
+  {
+    char buf[sizeof(expected)];
+    EXPECT_EQ(sizeof(expected) - 1,
+              demangle(typeid(folly_test::ThisIsAVeryLongStructureName),
+                       buf, sizeof(buf)));
+    EXPECT_STREQ(expected, buf);
+
+    EXPECT_EQ(sizeof(expected) - 1,
+              demangle(typeid(folly_test::ThisIsAVeryLongStructureName),
+                       buf, 11));
+    EXPECT_STREQ("folly_test", buf);
+  }
+}
+#endif
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/DeterministicSchedule.cpp b/faux-folly/folly/test/DeterministicSchedule.cpp
new file mode 100644
index 0000000..42cf192
--- /dev/null
+++ b/faux-folly/folly/test/DeterministicSchedule.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2015 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/test/DeterministicSchedule.h>
+#include <algorithm>
+#include <list>
+#include <mutex>
+#include <random>
+#include <utility>
+#include <unordered_map>
+#include <assert.h>
+
+namespace folly {
+namespace test {
+
+FOLLY_TLS sem_t* DeterministicSchedule::tls_sem;
+FOLLY_TLS DeterministicSchedule* DeterministicSchedule::tls_sched;
+FOLLY_TLS unsigned DeterministicSchedule::tls_threadId;
+
+// access is protected by futexLock
+static std::unordered_map<detail::Futex<DeterministicAtomic>*,
+                          std::list<std::pair<uint32_t, bool*>>> futexQueues;
+
+static std::mutex futexLock;
+
+DeterministicSchedule::DeterministicSchedule(
+    const std::function<int(int)>& scheduler)
+    : scheduler_(scheduler), nextThreadId_(1) {
+  assert(tls_sem == nullptr);
+  assert(tls_sched == nullptr);
+
+  tls_sem = new sem_t;
+  sem_init(tls_sem, 0, 1);
+  sems_.push_back(tls_sem);
+
+  tls_sched = this;
+}
+
+DeterministicSchedule::~DeterministicSchedule() {
+  assert(tls_sched == this);
+  assert(sems_.size() == 1);
+  assert(sems_[0] == tls_sem);
+  beforeThreadExit();
+}
+
+std::function<int(int)> DeterministicSchedule::uniform(long seed) {
+  auto rand = std::make_shared<std::ranlux48>(seed);
+  return [rand](size_t numActive) {
+    auto dist = std::uniform_int_distribution<int>(0, numActive - 1);
+    return dist(*rand);
+  };
+}
+
+struct UniformSubset {
+  UniformSubset(long seed, int subsetSize, int stepsBetweenSelect)
+      : uniform_(DeterministicSchedule::uniform(seed)),
+        subsetSize_(subsetSize),
+        stepsBetweenSelect_(stepsBetweenSelect),
+        stepsLeft_(0) {}
+
+  size_t operator()(size_t numActive) {
+    adjustPermSize(numActive);
+    if (stepsLeft_-- == 0) {
+      stepsLeft_ = stepsBetweenSelect_ - 1;
+      shufflePrefix();
+    }
+    return perm_[uniform_(std::min(numActive, subsetSize_))];
+  }
+
+ private:
+  std::function<int(int)> uniform_;
+  const size_t subsetSize_;
+  const int stepsBetweenSelect_;
+
+  int stepsLeft_;
+  // only the first subsetSize_ is properly randomized
+  std::vector<int> perm_;
+
+  void adjustPermSize(size_t numActive) {
+    if (perm_.size() > numActive) {
+      perm_.erase(std::remove_if(perm_.begin(), perm_.end(), [=](size_t x) {
+        return x >= numActive;
+      }), perm_.end());
+    } else {
+      while (perm_.size() < numActive) {
+        perm_.push_back(perm_.size());
+      }
+    }
+    assert(perm_.size() == numActive);
+  }
+
+  void shufflePrefix() {
+    for (size_t i = 0; i < std::min(perm_.size() - 1, subsetSize_); ++i) {
+      int j = uniform_(perm_.size() - i) + i;
+      std::swap(perm_[i], perm_[j]);
+    }
+  }
+};
+
+std::function<int(int)> DeterministicSchedule::uniformSubset(long seed,
+                                                             int n,
+                                                             int m) {
+  auto gen = std::make_shared<UniformSubset>(seed, n, m);
+  return [=](size_t numActive) { return (*gen)(numActive); };
+}
+
+void DeterministicSchedule::beforeSharedAccess() {
+  if (tls_sem) {
+    sem_wait(tls_sem);
+  }
+}
+
+void DeterministicSchedule::afterSharedAccess() {
+  auto sched = tls_sched;
+  if (!sched) {
+    return;
+  }
+
+  sem_post(sched->sems_[sched->scheduler_(sched->sems_.size())]);
+}
+
+int DeterministicSchedule::getRandNumber(int n) {
+  if (tls_sched) {
+    return tls_sched->scheduler_(n);
+  }
+  return std::rand() % n;
+}
+
+int DeterministicSchedule::getcpu(unsigned* cpu, unsigned* node, void* unused) {
+  if (!tls_threadId && tls_sched) {
+    beforeSharedAccess();
+    tls_threadId = tls_sched->nextThreadId_++;
+    afterSharedAccess();
+  }
+  if (cpu) {
+    *cpu = tls_threadId;
+  }
+  if (node) {
+    *node = tls_threadId;
+  }
+  return 0;
+}
+
+sem_t* DeterministicSchedule::beforeThreadCreate() {
+  sem_t* s = new sem_t;
+  sem_init(s, 0, 0);
+  beforeSharedAccess();
+  sems_.push_back(s);
+  afterSharedAccess();
+  return s;
+}
+
+void DeterministicSchedule::afterThreadCreate(sem_t* sem) {
+  assert(tls_sem == nullptr);
+  assert(tls_sched == nullptr);
+  tls_sem = sem;
+  tls_sched = this;
+  bool started = false;
+  while (!started) {
+    beforeSharedAccess();
+    if (active_.count(std::this_thread::get_id()) == 1) {
+      started = true;
+    }
+    afterSharedAccess();
+  }
+}
+
+void DeterministicSchedule::beforeThreadExit() {
+  assert(tls_sched == this);
+  beforeSharedAccess();
+  sems_.erase(std::find(sems_.begin(), sems_.end(), tls_sem));
+  active_.erase(std::this_thread::get_id());
+  if (sems_.size() > 0) {
+    FOLLY_TEST_DSCHED_VLOG("exiting");
+    afterSharedAccess();
+  }
+  sem_destroy(tls_sem);
+  delete tls_sem;
+  tls_sem = nullptr;
+  tls_sched = nullptr;
+}
+
+void DeterministicSchedule::join(std::thread& child) {
+  auto sched = tls_sched;
+  if (sched) {
+    bool done = false;
+    while (!done) {
+      beforeSharedAccess();
+      done = !sched->active_.count(child.get_id());
+      if (done) {
+        FOLLY_TEST_DSCHED_VLOG("joined " << std::hex << child.get_id());
+      }
+      afterSharedAccess();
+    }
+  }
+  child.join();
+}
+
+void DeterministicSchedule::post(sem_t* sem) {
+  beforeSharedAccess();
+  sem_post(sem);
+  FOLLY_TEST_DSCHED_VLOG("sem_post(" << sem << ")");
+  afterSharedAccess();
+}
+
+bool DeterministicSchedule::tryWait(sem_t* sem) {
+  beforeSharedAccess();
+  int rv = sem_trywait(sem);
+  int e = rv == 0 ? 0 : errno;
+  FOLLY_TEST_DSCHED_VLOG("sem_trywait(" << sem << ") = " << rv
+                                        << " errno=" << e);
+  afterSharedAccess();
+  if (rv == 0) {
+    return true;
+  } else {
+    assert(e == EAGAIN);
+    return false;
+  }
+}
+
+void DeterministicSchedule::wait(sem_t* sem) {
+  while (!tryWait(sem)) {
+    // we're not busy waiting because this is a deterministic schedule
+  }
+}
+}
+}
+
+namespace folly {
+namespace detail {
+
+using namespace test;
+using namespace std::chrono;
+
+template <>
+FutexResult Futex<DeterministicAtomic>::futexWaitImpl(
+    uint32_t expected,
+    time_point<system_clock>* absSystemTimeout,
+    time_point<steady_clock>* absSteadyTimeout,
+    uint32_t waitMask) {
+  bool hasTimeout = absSystemTimeout != nullptr || absSteadyTimeout != nullptr;
+  bool awoken = false;
+  FutexResult result = FutexResult::AWOKEN;
+  int futexErrno = 0;
+
+  DeterministicSchedule::beforeSharedAccess();
+  FOLLY_TEST_DSCHED_VLOG(this << ".futexWait(" << std::hex << expected
+                              << ", .., " << std::hex << waitMask
+                              << ") beginning..");
+  futexLock.lock();
+  if (data == expected) {
+    auto& queue = futexQueues[this];
+    queue.emplace_back(waitMask, &awoken);
+    auto ours = queue.end();
+    ours--;
+    while (!awoken) {
+      futexLock.unlock();
+      DeterministicSchedule::afterSharedAccess();
+      DeterministicSchedule::beforeSharedAccess();
+      futexLock.lock();
+
+      // Simulate spurious wake-ups, timeouts each time with
+      // a 10% probability if we haven't been woken up already
+      if (!awoken && hasTimeout &&
+          DeterministicSchedule::getRandNumber(100) < 10) {
+        assert(futexQueues.count(this) != 0 && &futexQueues[this] == &queue);
+        queue.erase(ours);
+        if (queue.empty()) {
+          futexQueues.erase(this);
+        }
+        // Simulate ETIMEDOUT 90% of the time and other failures
+        // remaining time
+        result = DeterministicSchedule::getRandNumber(100) >= 10
+                     ? FutexResult::TIMEDOUT
+                     : FutexResult::INTERRUPTED;
+        break;
+      }
+    }
+  } else {
+    result = FutexResult::VALUE_CHANGED;
+  }
+  futexLock.unlock();
+
+  char const* resultStr = "?";
+  switch (result) {
+  case FutexResult::AWOKEN:
+    resultStr = "AWOKEN";
+    break;
+  case FutexResult::TIMEDOUT:
+    resultStr = "TIMEDOUT";
+    break;
+  case FutexResult::INTERRUPTED:
+    resultStr = "INTERRUPTED";
+    break;
+  case FutexResult::VALUE_CHANGED:
+    resultStr = "VALUE_CHANGED";
+    break;
+  }
+  FOLLY_TEST_DSCHED_VLOG(this << ".futexWait(" << std::hex << expected
+                              << ", .., " << std::hex << waitMask << ") -> "
+                              << resultStr);
+  DeterministicSchedule::afterSharedAccess();
+  return result;
+}
+
+template <>
+int Futex<DeterministicAtomic>::futexWake(int count, uint32_t wakeMask) {
+  int rv = 0;
+  DeterministicSchedule::beforeSharedAccess();
+  futexLock.lock();
+  if (futexQueues.count(this) > 0) {
+    auto& queue = futexQueues[this];
+    auto iter = queue.begin();
+    while (iter != queue.end() && rv < count) {
+      auto cur = iter++;
+      if ((cur->first & wakeMask) != 0) {
+        *(cur->second) = true;
+        rv++;
+        queue.erase(cur);
+      }
+    }
+    if (queue.empty()) {
+      futexQueues.erase(this);
+    }
+  }
+  futexLock.unlock();
+  FOLLY_TEST_DSCHED_VLOG(this << ".futexWake(" << count << ", " << std::hex
+                              << wakeMask << ") -> " << rv);
+  DeterministicSchedule::afterSharedAccess();
+  return rv;
+}
+
+template <>
+CacheLocality const& CacheLocality::system<test::DeterministicAtomic>() {
+  static CacheLocality cache(CacheLocality::uniform(16));
+  return cache;
+}
+
+template <>
+const AccessSpreader<test::DeterministicAtomic>
+    AccessSpreader<test::DeterministicAtomic>::stripeByCore(
+        CacheLocality::system<>().numCachesByLevel.front());
+
+template <>
+const AccessSpreader<test::DeterministicAtomic>
+    AccessSpreader<test::DeterministicAtomic>::stripeByChip(
+        CacheLocality::system<>().numCachesByLevel.back());
+
+template <>
+AccessSpreaderArray<test::DeterministicAtomic, 128>
+    AccessSpreaderArray<test::DeterministicAtomic, 128>::sharedInstance = {};
+
+template <>
+Getcpu::Func AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc(
+    size_t numStripes) {
+  return &DeterministicSchedule::getcpu;
+}
+}
+}
diff --git a/faux-folly/folly/test/DeterministicSchedule.h b/faux-folly/folly/test/DeterministicSchedule.h
new file mode 100644
index 0000000..25cd80e
--- /dev/null
+++ b/faux-folly/folly/test/DeterministicSchedule.h
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2015 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 <atomic>
+#include <functional>
+#include <thread>
+#include <unordered_set>
+#include <vector>
+#include <boost/noncopyable.hpp>
+#include <semaphore.h>
+#include <errno.h>
+#include <assert.h>
+#include <glog/logging.h>
+
+#include <folly/ScopeGuard.h>
+#include <folly/detail/CacheLocality.h>
+#include <folly/detail/Futex.h>
+
+namespace folly {
+namespace test {
+
+// This is ugly, but better perf for DeterministicAtomic translates
+// directly to more states explored and tested
+#define FOLLY_TEST_DSCHED_VLOG(msg...)                                  \
+  do {                                                                  \
+    if (false) {                                                        \
+      VLOG(2) << std::hex << std::this_thread::get_id() << ": " << msg; \
+    }                                                                   \
+  } while (false)
+
+/**
+ * DeterministicSchedule coordinates the inter-thread communication of a
+ * set of threads under test, so that despite concurrency the execution is
+ * the same every time.  It works by stashing a reference to the schedule
+ * in a thread-local variable, then blocking all but one thread at a time.
+ *
+ * In order for DeterministicSchedule to work, it needs to intercept
+ * all inter-thread communication.  To do this you should use
+ * DeterministicAtomic<T> instead of std::atomic<T>, create threads
+ * using DeterministicSchedule::thread() instead of the std::thread
+ * constructor, DeterministicSchedule::join(thr) instead of thr.join(),
+ * and access semaphores via the helper functions in DeterministicSchedule.
+ * Locks are not yet supported, although they would be easy to add with
+ * the same strategy as the mapping of sem_wait.
+ *
+ * The actual schedule is defined by a function from n -> [0,n). At
+ * each step, the function will be given the number of active threads
+ * (n), and it returns the index of the thread that should be run next.
+ * Invocations of the scheduler function will be serialized, but will
+ * occur from multiple threads.  A good starting schedule is uniform(0).
+ */
+class DeterministicSchedule : boost::noncopyable {
+ public:
+  /**
+   * Arranges for the current thread (and all threads created by
+   * DeterministicSchedule::thread on a thread participating in this
+   * schedule) to participate in a deterministic schedule.
+   */
+  explicit DeterministicSchedule(const std::function<int(int)>& scheduler);
+
+  /** Completes the schedule. */
+  ~DeterministicSchedule();
+
+  /**
+   * Returns a scheduling function that randomly chooses one of the
+   * runnable threads at each step, with no history.  This implements
+   * a schedule that is equivalent to one in which the steps between
+   * inter-thread communication are random variables following a poisson
+   * distribution.
+   */
+  static std::function<int(int)> uniform(long seed);
+
+  /**
+   * Returns a scheduling function that chooses a subset of the active
+   * threads and randomly chooses a member of the subset as the next
+   * runnable thread.  The subset is chosen with size n, and the choice
+   * is made every m steps.
+   */
+  static std::function<int(int)> uniformSubset(long seed,
+                                               int n = 2,
+                                               int m = 64);
+
+  /** Obtains permission for the current thread to perform inter-thread
+   *  communication. */
+  static void beforeSharedAccess();
+
+  /** Releases permission for the current thread to perform inter-thread
+   *  communication. */
+  static void afterSharedAccess();
+
+  /** Launches a thread that will participate in the same deterministic
+   *  schedule as the current thread. */
+  template <typename Func, typename... Args>
+  static inline std::thread thread(Func&& func, Args&&... args) {
+    // TODO: maybe future versions of gcc will allow forwarding to thread
+    auto sched = tls_sched;
+    auto sem = sched ? sched->beforeThreadCreate() : nullptr;
+    auto child = std::thread([=](Args... a) {
+      if (sched) {
+        sched->afterThreadCreate(sem);
+        beforeSharedAccess();
+        FOLLY_TEST_DSCHED_VLOG("running");
+        afterSharedAccess();
+      }
+      SCOPE_EXIT {
+        if (sched) {
+          sched->beforeThreadExit();
+        }
+      };
+      func(a...);
+    }, args...);
+    if (sched) {
+      beforeSharedAccess();
+      sched->active_.insert(child.get_id());
+      FOLLY_TEST_DSCHED_VLOG("forked " << std::hex << child.get_id());
+      afterSharedAccess();
+    }
+    return child;
+  }
+
+  /** Calls child.join() as part of a deterministic schedule. */
+  static void join(std::thread& child);
+
+  /** Calls sem_post(sem) as part of a deterministic schedule. */
+  static void post(sem_t* sem);
+
+  /** Calls sem_trywait(sem) as part of a deterministic schedule, returning
+   *  true on success and false on transient failure. */
+  static bool tryWait(sem_t* sem);
+
+  /** Calls sem_wait(sem) as part of a deterministic schedule. */
+  static void wait(sem_t* sem);
+
+  /** Used scheduler_ to get a random number b/w [0, n). If tls_sched is
+   *  not set-up it falls back to std::rand() */
+  static int getRandNumber(int n);
+
+  /** Deterministic implemencation of getcpu */
+  static int getcpu(unsigned* cpu, unsigned* node, void* unused);
+
+ private:
+  static FOLLY_TLS sem_t* tls_sem;
+  static FOLLY_TLS DeterministicSchedule* tls_sched;
+  static FOLLY_TLS unsigned tls_threadId;
+
+  std::function<int(int)> scheduler_;
+  std::vector<sem_t*> sems_;
+  std::unordered_set<std::thread::id> active_;
+  unsigned nextThreadId_;
+
+  sem_t* beforeThreadCreate();
+  void afterThreadCreate(sem_t*);
+  void beforeThreadExit();
+};
+
+/**
+ * DeterministicAtomic<T> is a drop-in replacement std::atomic<T> that
+ * cooperates with DeterministicSchedule.
+ */
+template <typename T>
+struct DeterministicAtomic {
+  std::atomic<T> data;
+
+  DeterministicAtomic() = default;
+  ~DeterministicAtomic() = default;
+  DeterministicAtomic(DeterministicAtomic<T> const&) = delete;
+  DeterministicAtomic<T>& operator=(DeterministicAtomic<T> const&) = delete;
+
+  constexpr /* implicit */ DeterministicAtomic(T v) noexcept : data(v) {}
+
+  bool is_lock_free() const noexcept { return data.is_lock_free(); }
+
+  bool compare_exchange_strong(
+      T& v0, T v1, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    auto orig = v0;
+    bool rv = data.compare_exchange_strong(v0, v1, mo);
+    FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_strong(" << std::hex
+                                << orig << ", " << std::hex << v1 << ") -> "
+                                << rv << "," << std::hex << v0);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  bool compare_exchange_weak(
+      T& v0, T v1, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    auto orig = v0;
+    bool rv = data.compare_exchange_weak(v0, v1, mo);
+    FOLLY_TEST_DSCHED_VLOG(this << ".compare_exchange_weak(" << std::hex << orig
+                                << ", " << std::hex << v1 << ") -> " << rv
+                                << "," << std::hex << v0);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T exchange(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data.exchange(v, mo);
+    FOLLY_TEST_DSCHED_VLOG(this << ".exchange(" << std::hex << v << ") -> "
+                                << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  /* implicit */ operator T() const noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data;
+    FOLLY_TEST_DSCHED_VLOG(this << "() -> " << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T load(std::memory_order mo = std::memory_order_seq_cst) const noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data.load(mo);
+    FOLLY_TEST_DSCHED_VLOG(this << ".load() -> " << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator=(T v) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = (data = v);
+    FOLLY_TEST_DSCHED_VLOG(this << " = " << std::hex << v);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  void store(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    data.store(v, mo);
+    FOLLY_TEST_DSCHED_VLOG(this << ".store(" << std::hex << v << ")");
+    DeterministicSchedule::afterSharedAccess();
+  }
+
+  T operator++() noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = ++data;
+    FOLLY_TEST_DSCHED_VLOG(this << " pre++ -> " << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator++(int postDummy) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data++;
+    FOLLY_TEST_DSCHED_VLOG(this << " post++ -> " << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator--() noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = --data;
+    FOLLY_TEST_DSCHED_VLOG(this << " pre-- -> " << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator--(int postDummy) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data--;
+    FOLLY_TEST_DSCHED_VLOG(this << " post-- -> " << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator+=(T v) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = (data += v);
+    FOLLY_TEST_DSCHED_VLOG(this << " += " << std::hex << v << " -> " << std::hex
+                                << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T fetch_add(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data;
+    data += v;
+    FOLLY_TEST_DSCHED_VLOG(this << ".fetch_add(" << std::hex << v << ") -> "
+                                << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator-=(T v) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = (data -= v);
+    FOLLY_TEST_DSCHED_VLOG(this << " -= " << std::hex << v << " -> " << std::hex
+                                << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T fetch_sub(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data;
+    data -= v;
+    FOLLY_TEST_DSCHED_VLOG(this << ".fetch_sub(" << std::hex << v << ") -> "
+                                << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator&=(T v) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = (data &= v);
+    FOLLY_TEST_DSCHED_VLOG(this << " &= " << std::hex << v << " -> " << std::hex
+                                << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T fetch_and(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data;
+    data &= v;
+    FOLLY_TEST_DSCHED_VLOG(this << ".fetch_and(" << std::hex << v << ") -> "
+                                << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator|=(T v) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = (data |= v);
+    FOLLY_TEST_DSCHED_VLOG(this << " |= " << std::hex << v << " -> " << std::hex
+                                << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T fetch_or(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data;
+    data |= v;
+    FOLLY_TEST_DSCHED_VLOG(this << ".fetch_or(" << std::hex << v << ") -> "
+                                << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T operator^=(T v) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = (data ^= v);
+    FOLLY_TEST_DSCHED_VLOG(this << " ^= " << std::hex << v << " -> " << std::hex
+                                << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+
+  T fetch_xor(T v, std::memory_order mo = std::memory_order_seq_cst) noexcept {
+    DeterministicSchedule::beforeSharedAccess();
+    T rv = data;
+    data ^= v;
+    FOLLY_TEST_DSCHED_VLOG(this << ".fetch_xor(" << std::hex << v << ") -> "
+                                << std::hex << rv);
+    DeterministicSchedule::afterSharedAccess();
+    return rv;
+  }
+};
+}
+} // namespace folly::test
+
+/* Specialization declarations */
+
+namespace folly {
+namespace detail {
+
+template <>
+int Futex<test::DeterministicAtomic>::futexWake(int count, uint32_t wakeMask);
+
+template <>
+FutexResult Futex<test::DeterministicAtomic>::futexWaitImpl(
+    uint32_t expected,
+    std::chrono::time_point<std::chrono::system_clock>* absSystemTime,
+    std::chrono::time_point<std::chrono::steady_clock>* absSteadyTime,
+    uint32_t waitMask);
+
+template <>
+Getcpu::Func AccessSpreader<test::DeterministicAtomic>::pickGetcpuFunc(
+    size_t numStripes);
+}
+} // namespace folly::detail
diff --git a/faux-folly/folly/test/DeterministicScheduleTest.cpp b/faux-folly/folly/test/DeterministicScheduleTest.cpp
new file mode 100644
index 0000000..1e36fb5
--- /dev/null
+++ b/faux-folly/folly/test/DeterministicScheduleTest.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 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/test/DeterministicSchedule.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using namespace folly::test;
+
+TEST(DeterministicSchedule, uniform) {
+  auto p = DeterministicSchedule::uniform(0);
+  int buckets[10] = {};
+  for (int i = 0; i < 100000; ++i) {
+    buckets[p(10)]++;
+  }
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_TRUE(buckets[i] > 9000);
+  }
+}
+
+TEST(DeterministicSchedule, uniformSubset) {
+  auto ps = DeterministicSchedule::uniformSubset(0, 3, 100);
+  int buckets[10] = {};
+  std::set<int> seen;
+  for (int i = 0; i < 100000; ++i) {
+    if (i > 0 && (i % 100) == 0) {
+      EXPECT_EQ(seen.size(), 3);
+      seen.clear();
+    }
+    int x = ps(10);
+    seen.insert(x);
+    EXPECT_TRUE(seen.size() <= 3);
+    buckets[x]++;
+  }
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_TRUE(buckets[i] > 9000);
+  }
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/DynamicConverterTest.cpp b/faux-folly/folly/test/DynamicConverterTest.cpp
new file mode 100644
index 0000000..031f650
--- /dev/null
+++ b/faux-folly/folly/test/DynamicConverterTest.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Nicholas Ormrod <njormrod@fb.com>
+
+#include <folly/DynamicConverter.h>
+
+#include <algorithm>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include <map>
+#include <vector>
+
+#include <folly/Benchmark.h>
+
+using namespace folly;
+using namespace folly::dynamicconverter_detail;
+
+TEST(DynamicConverter, template_metaprogramming) {
+  struct A {};
+
+  bool c1f = is_container<int>::value;
+  bool c2f = is_container<std::pair<int, int>>::value;
+  bool c3f = is_container<A>::value;
+  bool c4f = class_is_container<A>::value;
+
+  bool c1t = is_container<std::vector<int>>::value;
+  bool c2t = is_container<std::set<int>>::value;
+  bool c3t = is_container<std::map<int, int>>::value;
+  bool c4t = class_is_container<std::vector<A>>::value;
+
+  EXPECT_EQ(c1f, false);
+  EXPECT_EQ(c2f, false);
+  EXPECT_EQ(c3f, false);
+  EXPECT_EQ(c4f, false);
+  EXPECT_EQ(c1t, true);
+  EXPECT_EQ(c2t, true);
+  EXPECT_EQ(c3t, true);
+  EXPECT_EQ(c4t, true);
+
+
+  bool m1f = is_map<int>::value;
+  bool m2f = is_map<std::set<int>>::value;
+
+  bool m1t = is_map<std::map<int, int>>::value;
+
+  EXPECT_EQ(m1f, false);
+  EXPECT_EQ(m2f, false);
+  EXPECT_EQ(m1t, true);
+
+
+  bool r1f = is_range<int>::value;
+
+  bool r1t = is_range<std::set<int>>::value;
+  bool r2t = is_range<std::vector<int>>::value;
+
+  EXPECT_EQ(r1f, false);
+  EXPECT_EQ(r1t, true);
+  EXPECT_EQ(r2t, true);
+}
+
+TEST(DynamicConverter, arithmetic_types) {
+  dynamic d1 = 12;
+  auto i1 = convertTo<int>(d1);
+  EXPECT_EQ(i1, 12);
+
+  dynamic d2 = 123456789012345;
+  auto i2 = convertTo<int64_t>(d2);
+  EXPECT_EQ(i2, 123456789012345);
+
+  dynamic d4 = 3.141;
+  auto i4 = convertTo<float>(d4);
+  EXPECT_EQ((int)(i4*100), 314);
+
+  dynamic d5 = true;
+  auto i5 = convertTo<bool>(d5);
+  EXPECT_EQ(i5, true);
+
+  dynamic d6 = 15;
+  const auto i6 = convertTo<const int>(d6);
+  EXPECT_EQ(i6, 15);
+
+  dynamic d7 = "87";
+  auto i7 = convertTo<int>(d7);
+  EXPECT_EQ(i7, 87);
+
+  dynamic d8 = "false";
+  auto i8 = convertTo<bool>(d8);
+  EXPECT_EQ(i8, false);
+}
+
+TEST(DynamicConverter, simple_builtins) {
+  dynamic d1 = "Haskell";
+  auto i1 = convertTo<folly::fbstring>(d1);
+  EXPECT_EQ(i1, "Haskell");
+
+  dynamic d2 = 13;
+  auto i2 = convertTo<std::string>(d2);
+  EXPECT_EQ(i2, "13");
+
+  dynamic d3 = { 12, "Scala" };
+  auto i3 = convertTo<std::pair<int, std::string>>(d3);
+  EXPECT_EQ(i3.first, 12);
+  EXPECT_EQ(i3.second, "Scala");
+
+  dynamic d4 = dynamic::object("C", "C++");
+  auto i4 = convertTo<std::pair<std::string, folly::fbstring>>(d4);
+  EXPECT_EQ(i4.first, "C");
+  EXPECT_EQ(i4.second, "C++");
+}
+
+TEST(DynamicConverter, simple_fbvector) {
+  dynamic d1 = { 1, 2, 3 };
+  auto i1 = convertTo<folly::fbvector<int>>(d1);
+  decltype(i1) i1b = { 1, 2, 3 };
+  EXPECT_EQ(i1, i1b);
+}
+
+TEST(DynamicConverter, simple_container) {
+  dynamic d1 = { 1, 2, 3 };
+  auto i1 = convertTo<std::vector<int>>(d1);
+  decltype(i1) i1b = { 1, 2, 3 };
+  EXPECT_EQ(i1, i1b);
+
+  dynamic d2 = { 1, 3, 5, 2, 4 };
+  auto i2 = convertTo<std::set<int>>(d2);
+  decltype(i2) i2b = { 1, 2, 3, 5, 4 };
+  EXPECT_EQ(i2, i2b);
+}
+
+TEST(DynamicConverter, simple_map) {
+  dynamic d1 = dynamic::object(1, "one")(2, "two");
+  auto i1 = convertTo<std::map<int, std::string>>(d1);
+  decltype(i1) i1b = { { 1, "one" }, { 2, "two" } };
+  EXPECT_EQ(i1, i1b);
+
+  dynamic d2 = { { 3, "three" }, { 4, "four" } };
+  auto i2 = convertTo<std::unordered_map<int, std::string>>(d2);
+  decltype(i2) i2b = { { 3, "three" }, { 4, "four" } };
+  EXPECT_EQ(i2, i2b);
+}
+
+TEST(DynamicConverter, map_keyed_by_string) {
+  dynamic d1 = dynamic::object("1", "one")("2", "two");
+  auto i1 = convertTo<std::map<std::string, std::string>>(d1);
+  decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
+  EXPECT_EQ(i1, i1b);
+
+  dynamic d2 = { { "3", "three" }, { "4", "four" } };
+  auto i2 = convertTo<std::unordered_map<std::string, std::string>>(d2);
+  decltype(i2) i2b = { { "3", "three" }, { "4", "four" } };
+  EXPECT_EQ(i2, i2b);
+}
+
+TEST(DynamicConverter, map_to_vector_of_pairs) {
+  dynamic d1 = dynamic::object("1", "one")("2", "two");
+  auto i1 = convertTo<std::vector<std::pair<std::string, std::string>>>(d1);
+  std::sort(i1.begin(), i1.end());
+  decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
+  EXPECT_EQ(i1, i1b);
+}
+
+TEST(DynamicConverter, nested_containers) {
+  dynamic d1 = { { 1 }, { }, { 2, 3 } };
+  auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1);
+  decltype(i1) i1b = { { 1 }, { }, { 2, 3 } };
+  EXPECT_EQ(i1, i1b);
+
+  dynamic h2a = { "3", ".", "1", "4" };
+  dynamic h2b = { "2", ".", "7", "2" };
+  dynamic d2 = dynamic::object(3.14, h2a)(2.72, h2b);
+  auto i2 = convertTo<std::map<double, std::vector<folly::fbstring>>>(d2);
+  decltype(i2) i2b =
+    { { 3.14, { "3", ".", "1", "4" } },
+      { 2.72, { "2", ".", "7", "2" } } };
+  EXPECT_EQ(i2, i2b);
+}
+
+struct A {
+  int i;
+  bool operator==(const A & o) const { return i == o.i; }
+};
+namespace folly {
+template <> struct DynamicConverter<A> {
+  static A convert(const dynamic & d) {
+    return { convertTo<int>(d["i"]) };
+  }
+};
+}
+TEST(DynamicConverter, custom_class) {
+  dynamic d1 = dynamic::object("i", 17);
+  auto i1 = convertTo<A>(d1);
+  EXPECT_EQ(i1.i, 17);
+
+  dynamic d2 = { dynamic::object("i", 18), dynamic::object("i", 19) };
+  auto i2 = convertTo<std::vector<A>>(d2);
+  decltype(i2) i2b = { { 18 }, { 19 } };
+  EXPECT_EQ(i2, i2b);
+}
+
+TEST(DynamicConverter, crazy) {
+  // we are going to create a vector<unordered_map<bool, T>>
+  // we will construct some of the maps from dynamic objects,
+  //   some from a vector of KV pairs.
+  // T will be vector<set<string>>
+
+  std::set<std::string>
+    s1 = { "a", "e", "i", "o", "u" },
+    s2 = { "2", "3", "5", "7" },
+    s3 = { "Hello", "World" };
+
+  std::vector<std::set<std::string>>
+    v1 = {},
+    v2 = { s1, s2 },
+    v3 = { s3 };
+
+  std::unordered_map<bool, std::vector<std::set<std::string>>>
+    m1 = { { true, v1 }, { false, v2 } },
+    m2 = { { true, v3 } };
+
+  std::vector<std::unordered_map<bool, std::vector<std::set<std::string>>>>
+    f1 = { m1, m2 };
+
+
+  dynamic
+    ds1 = { "a", "e", "i", "o", "u" },
+    ds2 = { "2", "3", "5", "7" },
+    ds3 = { "Hello", "World" };
+
+  dynamic
+    dv1 = {},
+    dv2 = { ds1, ds2 },
+    dv3({ ds3 });
+
+  dynamic
+    dm1 = dynamic::object(true, dv1)(false, dv2),
+    dm2 = { { true, dv3 } };
+
+  dynamic
+    df1 = { dm1, dm2 };
+
+
+  auto i = convertTo<std::vector<std::unordered_map<bool, std::vector<
+          std::set<std::string>>>>>(df1); // yes, that is 5 close-chevrons
+
+  EXPECT_EQ(f1, i);
+}
+
+TEST(DynamicConverter, consts) {
+  dynamic d1 = 7.5;
+  auto i1 = convertTo<const double>(d1);
+  EXPECT_EQ(7.5, i1);
+
+  dynamic d2 = "Hello";
+  auto i2 = convertTo<const std::string>(d2);
+  decltype(i2) i2b = "Hello";
+  EXPECT_EQ(i2b, i2);
+
+  dynamic d3 = true;
+  auto i3 = convertTo<const bool>(d3);
+  EXPECT_EQ(true, i3);
+
+  dynamic d4 = "true";
+  auto i4 = convertTo<const bool>(d4);
+  EXPECT_EQ(true, i4);
+
+  dynamic d5 = { 1, 2 };
+  auto i5 = convertTo<const std::pair<const int, const int>>(d5);
+  decltype(i5) i5b = { 1, 2 };
+  EXPECT_EQ(i5b, i5);
+}
+
+struct Token {
+  int kind_;
+  fbstring lexeme_;
+
+  explicit Token(int kind, const fbstring& lexeme)
+    : kind_(kind), lexeme_(lexeme) {}
+};
+
+namespace folly {
+template <> struct DynamicConverter<Token> {
+  static Token convert(const dynamic& d) {
+    int k = convertTo<int>(d["KIND"]);
+    fbstring lex = convertTo<fbstring>(d["LEXEME"]);
+    return Token(k, lex);
+  }
+};
+}
+
+TEST(DynamicConverter, example) {
+  dynamic d1 = dynamic::object("KIND", 2)("LEXEME", "a token");
+  auto i1 = convertTo<Token>(d1);
+  EXPECT_EQ(i1.kind_, 2);
+  EXPECT_EQ(i1.lexeme_, "a token");
+}
+
+TEST(DynamicConverter, construct) {
+  using std::vector;
+  using std::map;
+  using std::pair;
+  using std::string;
+  {
+    vector<int> c { 1, 2, 3 };
+    dynamic d = { 1, 2, 3 };
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<int, int> c { { 2, 4 }, { 3, 9 } };
+    dynamic d = dynamic::object(2, 4)(3, 9);
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<string, string> c { { "a", "b" } };
+    dynamic d = dynamic::object("a", "b");
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<string, pair<string, int>> c { { "a", { "b", 3 } } };
+    dynamic d = dynamic::object("a", dynamic { "b", 3 });
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<string, pair<string, int>> c { { "a", { "b", 3 } } };
+    dynamic d = dynamic::object("a", dynamic { "b", 3 });
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    vector<int> vi { 2, 3, 4, 5 };
+    auto c = std::make_pair(range(vi.begin(), vi.begin() + 3),
+                            range(vi.begin() + 1, vi.begin() + 4));
+    dynamic d = { { 2, 3, 4 }, { 3, 4, 5 } };
+    EXPECT_EQ(d, toDynamic(c));
+  }
+}
+
+TEST(DynamicConverter, errors) {
+  const auto int32Over =
+    static_cast<int64_t>(std::numeric_limits<int32_t>().max()) + 1;
+  const auto floatOver =
+    static_cast<double>(std::numeric_limits<float>().max()) * 2;
+
+  dynamic d1 = int32Over;
+  EXPECT_THROW(convertTo<int32_t>(d1), std::range_error);
+
+  dynamic d2 = floatOver;
+  EXPECT_THROW(convertTo<float>(d2), std::range_error);
+}
+
+int main(int argc, char ** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/DynamicTest.cpp b/faux-folly/folly/test/DynamicTest.cpp
new file mode 100644
index 0000000..f4dbe32
--- /dev/null
+++ b/faux-folly/folly/test/DynamicTest.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2015 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/dynamic.h>
+
+#include <boost/next_prior.hpp>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using folly::dynamic;
+
+TEST(Dynamic, ObjectBasics) {
+  dynamic obj = dynamic::object("a", false);
+  EXPECT_EQ(obj.at("a"), false);
+  EXPECT_EQ(obj.size(), 1);
+  obj.insert("a", true);
+  EXPECT_EQ(obj.size(), 1);
+  EXPECT_EQ(obj.at("a"), true);
+  obj.at("a") = nullptr;
+  EXPECT_EQ(obj.size(), 1);
+  EXPECT_TRUE(obj.at("a") == nullptr);
+
+  dynamic newObject = dynamic::object;
+
+  newObject["z"] = 12;
+  EXPECT_EQ(newObject.size(), 1);
+  newObject["a"] = true;
+  EXPECT_EQ(newObject.size(), 2);
+
+  EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
+  EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
+  std::vector<std::pair<folly::fbstring, dynamic>> found;
+  found.emplace_back(newObject.keys().begin()->asString(),
+                     *newObject.values().begin());
+
+  EXPECT_EQ(*boost::next(newObject.keys().begin()),
+            boost::next(newObject.items().begin())->first);
+  EXPECT_EQ(*boost::next(newObject.values().begin()),
+            boost::next(newObject.items().begin())->second);
+  found.emplace_back(boost::next(newObject.keys().begin())->asString(),
+                     *boost::next(newObject.values().begin()));
+
+  std::sort(found.begin(), found.end());
+
+  EXPECT_EQ("a", found[0].first);
+  EXPECT_TRUE(found[0].second.asBool());
+
+  EXPECT_EQ("z", found[1].first);
+  EXPECT_EQ(12, found[1].second.asInt());
+
+  dynamic obj2 = dynamic::object;
+  EXPECT_TRUE(obj2.isObject());
+
+  dynamic d3 = nullptr;
+  EXPECT_TRUE(d3 == nullptr);
+  d3 = dynamic::object;
+  EXPECT_TRUE(d3.isObject());
+  d3["foo"] = { 1, 2, 3 };
+  EXPECT_EQ(d3.count("foo"), 1);
+
+  d3[123] = 321;
+  EXPECT_EQ(d3.at(123), 321);
+
+  d3["123"] = 42;
+  EXPECT_EQ(d3.at("123"), 42);
+  EXPECT_EQ(d3.at(123), 321);
+
+  dynamic objInsert = folly::dynamic::object();
+  dynamic objA = folly::dynamic::object("1", "2");
+  dynamic objB = folly::dynamic::object("1", "2");
+
+  objInsert.insert("1", std::move(objA));
+  objInsert.insert("1", std::move(objB));
+
+  EXPECT_EQ(objInsert.find("1")->second.size(), 1);
+
+  // We don't allow objects as keys in objects.
+  EXPECT_ANY_THROW(newObject[d3] = 12);
+}
+
+TEST(Dynamic, ObjectErase) {
+  dynamic obj = dynamic::object("key1", "val")
+                               ("key2", "val2");
+  EXPECT_EQ(obj.count("key1"), 1);
+  EXPECT_EQ(obj.count("key2"), 1);
+  EXPECT_EQ(obj.erase("key1"), 1);
+  EXPECT_EQ(obj.count("key1"), 0);
+  EXPECT_EQ(obj.count("key2"), 1);
+  EXPECT_EQ(obj.erase("key1"), 0);
+  obj["key1"] = 12;
+  EXPECT_EQ(obj.count("key1"), 1);
+  EXPECT_EQ(obj.count("key2"), 1);
+  auto it = obj.find("key2");
+  obj.erase(it);
+  EXPECT_EQ(obj.count("key1"), 1);
+  EXPECT_EQ(obj.count("key2"), 0);
+
+  obj["asd"] = 42.0;
+  obj["foo"] = 42.0;
+  EXPECT_EQ(obj.size(), 3);
+  auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
+  EXPECT_TRUE(ret == obj.items().end());
+  EXPECT_EQ(obj.size(), 1);
+  obj.erase(obj.items().begin());
+  EXPECT_TRUE(obj.empty());
+}
+
+TEST(Dynamic, ArrayErase) {
+  dynamic arr = { 1, 2, 3, 4, 5, 6 };
+
+  EXPECT_THROW(arr.erase(1), std::exception);
+  EXPECT_EQ(arr.size(), 6);
+  EXPECT_EQ(arr[0], 1);
+  arr.erase(arr.begin());
+  EXPECT_EQ(arr.size(), 5);
+
+  arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
+  EXPECT_EQ(arr.size(), 2);
+  EXPECT_EQ(arr[0], 2);
+  EXPECT_EQ(arr[1], 6);
+}
+
+TEST(Dynamic, StringBasics) {
+  dynamic str = "hello world";
+  EXPECT_EQ(11, str.size());
+  EXPECT_FALSE(str.empty());
+  str = "";
+  EXPECT_TRUE(str.empty());
+}
+
+TEST(Dynamic, ArrayBasics) {
+  dynamic array = { 1, 2, 3 };
+  EXPECT_EQ(array.size(), 3);
+  EXPECT_EQ(array.at(0), 1);
+  EXPECT_EQ(array.at(1), 2);
+  EXPECT_EQ(array.at(2), 3);
+
+  EXPECT_ANY_THROW(array.at(-1));
+  EXPECT_ANY_THROW(array.at(3));
+
+  array.push_back("foo");
+  EXPECT_EQ(array.size(), 4);
+
+  array.resize(12, "something");
+  EXPECT_EQ(array.size(), 12);
+  EXPECT_EQ(array[11], "something");
+}
+
+TEST(Dynamic, DeepCopy) {
+  dynamic val = { "foo", "bar", { "foo1", "bar1" } };
+  EXPECT_EQ(val.at(2).at(0), "foo1");
+  EXPECT_EQ(val.at(2).at(1), "bar1");
+  dynamic val2 = val;
+  EXPECT_EQ(val2.at(2).at(0), "foo1");
+  EXPECT_EQ(val2.at(2).at(1), "bar1");
+  EXPECT_EQ(val.at(2).at(0), "foo1");
+  EXPECT_EQ(val.at(2).at(1), "bar1");
+  val2.at(2).at(0) = "foo3";
+  val2.at(2).at(1) = "bar3";
+  EXPECT_EQ(val.at(2).at(0), "foo1");
+  EXPECT_EQ(val.at(2).at(1), "bar1");
+  EXPECT_EQ(val2.at(2).at(0), "foo3");
+  EXPECT_EQ(val2.at(2).at(1), "bar3");
+
+  dynamic obj = dynamic::object("a", "b")
+                               ("c", {"d", "e", "f"})
+                               ;
+  EXPECT_EQ(obj.at("a"), "b");
+  dynamic obj2 = obj;
+  obj2.at("a") = {1, 2, 3};
+  EXPECT_EQ(obj.at("a"), "b");
+  dynamic expected = {1, 2, 3};
+  EXPECT_EQ(obj2.at("a"), expected);
+}
+
+TEST(Dynamic, Operator) {
+  bool caught = false;
+  try {
+    dynamic d1 = dynamic::object;
+    dynamic d2 = dynamic::object;
+    auto foo = d1 < d2;
+  } catch (std::exception const& e) {
+    caught = true;
+  }
+  EXPECT_TRUE(caught);
+
+  dynamic foo = "asd";
+  dynamic bar = "bar";
+  dynamic sum = foo + bar;
+  EXPECT_EQ(sum, "asdbar");
+
+  dynamic some = 12;
+  dynamic nums = 4;
+  dynamic math = some / nums;
+  EXPECT_EQ(math, 3);
+}
+
+TEST(Dynamic, Conversions) {
+  dynamic str = "12.0";
+  EXPECT_EQ(str.asDouble(), 12.0);
+  EXPECT_ANY_THROW(str.asInt());
+  EXPECT_ANY_THROW(str.asBool());
+
+  str = "12";
+  EXPECT_EQ(str.asInt(), 12);
+  EXPECT_EQ(str.asDouble(), 12.0);
+  str = "0";
+  EXPECT_EQ(str.asBool(), false);
+  EXPECT_EQ(str.asInt(), 0);
+  EXPECT_EQ(str.asDouble(), 0);
+  EXPECT_EQ(str.asString(), "0");
+
+  dynamic num = 12;
+  EXPECT_EQ("12", num.asString());
+  EXPECT_EQ(12.0, num.asDouble());
+}
+
+TEST(Dynamic, GetSetDefaultTest) {
+  dynamic d1 = dynamic::object("foo", "bar");
+  EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
+  EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
+
+  dynamic d2 = dynamic::object("foo", "bar");
+  EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
+  d2.setDefault("bar", dynamic({})).push_back(42);
+  EXPECT_EQ(d2["bar"][0], 42);
+
+  dynamic d3 = dynamic::object, empty = dynamic::object;
+  EXPECT_EQ(d3.getDefault("foo"), empty);
+  d3.setDefault("foo")["bar"] = "baz";
+  EXPECT_EQ(d3["foo"]["bar"], "baz");
+
+  // we do not allow getDefault/setDefault on arrays
+  dynamic d4 = dynamic({});
+  EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
+  EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
+}
+
+TEST(Dynamic, ObjectForwarding) {
+  // Make sure dynamic::object can be constructed the same way as any
+  // dynamic.
+  dynamic d = dynamic::object("asd", {"foo", "bar"});
+  dynamic d2 = dynamic::object("key2", {"value", "words"})
+                              ("key", "value1");
+}
+
+TEST(Dynamic, GetPtr) {
+  dynamic array = { 1, 2, "three" };
+  EXPECT_TRUE(array.get_ptr(0));
+  EXPECT_FALSE(array.get_ptr(-1));
+  EXPECT_FALSE(array.get_ptr(3));
+  EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
+  const dynamic& carray = array;
+  EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
+
+  dynamic object = dynamic::object("one", 1)("two", 2);
+  EXPECT_TRUE(object.get_ptr("one"));
+  EXPECT_FALSE(object.get_ptr("three"));
+  EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
+  *object.get_ptr("one") = 11;
+  EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
+  const dynamic& cobject = object;
+  EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
+}
+
+TEST(Dynamic, Assignment) {
+  const dynamic ds[] = { { 1, 2, 3 },
+                         dynamic::object("a", true),
+                         24,
+                         26.5,
+                         true,
+                         "hello", };
+  const dynamic dd[] = { { 5, 6 },
+                         dynamic::object("t", "T")(1, 7),
+                         9000,
+                         3.14159,
+                         false,
+                         "world", };
+  for (const auto& source : ds) {
+    for (const auto& dest : dd) {
+      dynamic tmp(dest);
+      EXPECT_EQ(tmp, dest);
+      tmp = source;
+      EXPECT_EQ(tmp, source);
+    }
+  }
+}
+
+folly::fbstring make_long_string() {
+  return folly::fbstring(100, 'a');
+}
+
+TEST(Dynamic, GetDefault) {
+  const auto s = make_long_string();
+  dynamic ds(s);
+  dynamic tmp(s);
+  dynamic d1 = dynamic::object("key1", s);
+  dynamic d2 = dynamic::object("key2", s);
+  dynamic d3 = dynamic::object("key3", s);
+  dynamic d4 = dynamic::object("key4", s);
+  // lvalue - lvalue
+  dynamic ayy("ayy");
+  EXPECT_EQ(ds, d1.getDefault("key1", ayy));
+  EXPECT_EQ(ds, d1.getDefault("key1", ayy));
+  EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
+  EXPECT_EQ(ds, tmp);
+  // lvalue - rvalue
+  EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
+  EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
+  EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
+  EXPECT_NE(ds, tmp);
+  // rvalue - lvalue
+  tmp = s;
+  EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
+  EXPECT_NE(ds, d1["key1"]);
+  EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
+  EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
+  EXPECT_EQ(ds, tmp);
+  // rvalue - rvalue
+  EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
+  EXPECT_NE(ds, d3["key3"]);
+  EXPECT_EQ(ds, tmp);
+  EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
+  EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
+  EXPECT_NE(ds, tmp);
+}
+
+TEST(Dynamic, GetString) {
+  const dynamic c(make_long_string());
+  dynamic d(make_long_string());
+  dynamic m(make_long_string());
+
+  auto s = make_long_string();
+
+  EXPECT_EQ(s, c.getString());
+  EXPECT_EQ(s, c.getString());
+
+  d.getString() += " hello";
+  EXPECT_EQ(s + " hello", d.getString());
+  EXPECT_EQ(s + " hello", d.getString());
+
+  EXPECT_EQ(s, std::move(m).getString());
+  EXPECT_NE(dynamic(s), m);
+}
+
+TEST(Dynamic, GetSmallThings) {
+  const dynamic cint(5);
+  const dynamic cdouble(5.0);
+  const dynamic cbool(true);
+  dynamic dint(5);
+  dynamic ddouble(5.0);
+  dynamic dbool(true);
+  dynamic mint(5);
+  dynamic mdouble(5.0);
+  dynamic mbool(true);
+
+  EXPECT_EQ(5, cint.getInt());
+  dint.getInt() = 6;
+  EXPECT_EQ(6, dint.getInt());
+  EXPECT_EQ(5, std::move(mint).getInt());
+
+  EXPECT_EQ(5.0, cdouble.getDouble());
+  ddouble.getDouble() = 6.0;
+  EXPECT_EQ(6.0, ddouble.getDouble());
+  EXPECT_EQ(5.0, std::move(mdouble).getDouble());
+
+  EXPECT_EQ(true, cbool.getBool());
+  dbool.getBool() = false;
+  EXPECT_FALSE(dbool.getBool());
+  EXPECT_EQ(true, std::move(mbool).getBool());
+}
+
+TEST(Dynamic, At) {
+  const dynamic cd = dynamic::object("key1", make_long_string());
+  dynamic dd = dynamic::object("key1", make_long_string());
+  dynamic md = dynamic::object("key1", make_long_string());
+
+  dynamic ds(make_long_string());
+  EXPECT_EQ(ds, cd.at("key1"));
+  EXPECT_EQ(ds, cd.at("key1"));
+
+  dd.at("key1").getString() += " hello";
+  EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
+  EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
+
+  EXPECT_EQ(ds, std::move(md).at("key1"));
+  EXPECT_NE(ds, md.at("key1"));
+}
+
+TEST(Dynamic, Brackets) {
+  const dynamic cd = dynamic::object("key1", make_long_string());
+  dynamic dd = dynamic::object("key1", make_long_string());
+  dynamic md = dynamic::object("key1", make_long_string());
+
+  dynamic ds(make_long_string());
+  EXPECT_EQ(ds, cd["key1"]);
+  EXPECT_EQ(ds, cd["key1"]);
+
+  dd["key1"].getString() += " hello";
+  EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
+  EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
+
+  EXPECT_EQ(ds, std::move(md)["key1"]);
+  EXPECT_NE(ds, md["key1"]);
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/EndianTest.cpp b/faux-folly/folly/test/EndianTest.cpp
new file mode 100644
index 0000000..26847b4
--- /dev/null
+++ b/faux-folly/folly/test/EndianTest.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 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/Bits.h>
+
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+TEST(Endian, Basic) {
+  uint8_t v8 = 0x12;
+  uint8_t v8s = 0x12;
+  uint16_t v16 = 0x1234;
+  uint16_t v16s = 0x3412;
+  uint32_t v32 = 0x12345678;
+  uint32_t v32s = 0x78563412;
+  uint64_t v64 = 0x123456789abcdef0ULL;
+  uint64_t v64s = 0xf0debc9a78563412ULL;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+#define GEN1(sz) \
+  EXPECT_EQ(v##sz, Endian::little(v##sz)); \
+  EXPECT_EQ(v##sz, Endian::little##sz(v##sz)); \
+  EXPECT_EQ(v##sz##s, Endian::big(v##sz)); \
+  EXPECT_EQ(v##sz##s, Endian::big##sz(v##sz));
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+#define GEN1(sz) \
+  EXPECT_EQ(v##sz##s, Endian::little(v##sz)); \
+  EXPECT_EQ(v##sz##s, Endian::little##sz(v##sz)); \
+  EXPECT_EQ(v##sz, Endian::big(v##sz)); \
+  EXPECT_EQ(v##sz, Endian::big##sz(v##sz));
+
+#else
+# error Your machine uses a weird endianness!
+#endif  /* __BYTE_ORDER__ */
+
+#define GEN(sz) \
+  EXPECT_EQ(v##sz##s, Endian::swap(v##sz)); \
+  EXPECT_EQ(v##sz##s, Endian::swap##sz(v##sz)); \
+  GEN1(sz);
+
+  GEN(8);
+  GEN(16)
+  GEN(32)
+  GEN(64)
+
+#undef GEN
+#undef GEN1
+}
diff --git a/faux-folly/folly/test/ExceptionTest.cpp b/faux-folly/folly/test/ExceptionTest.cpp
new file mode 100644
index 0000000..635d20f
--- /dev/null
+++ b/faux-folly/folly/test/ExceptionTest.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 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/Exception.h>
+
+#include <cstdio>
+#include <memory>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+namespace folly { namespace test {
+
+#define EXPECT_SYSTEM_ERROR(statement, err, msg) \
+  try { \
+    statement; \
+    ADD_FAILURE() << "Didn't throw"; \
+  } catch (const std::system_error& e) { \
+    std::system_error expected(err, std::system_category(), msg); \
+    EXPECT_STREQ(expected.what(), e.what()); \
+  } catch (...) { \
+    ADD_FAILURE() << "Threw a different type"; \
+  }
+
+
+TEST(ExceptionTest, Simple) {
+  // Make sure errno isn't used when we don't want it to, set it to something
+  // else than what we set when we call Explicit functions
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({throwSystemErrorExplicit(EIO, "hello");},
+                      EIO, "hello");
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({throwSystemErrorExplicit(EIO, "hello", " world");},
+                      EIO, "hello world");
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({throwSystemError("hello", " world");},
+                      ERANGE, "hello world");
+
+  EXPECT_NO_THROW({checkPosixError(0, "hello", " world");});
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({checkPosixError(EIO, "hello", " world");},
+                      EIO, "hello world");
+
+  EXPECT_NO_THROW({checkKernelError(0, "hello", " world");});
+  EXPECT_NO_THROW({checkKernelError(EIO, "hello", " world");});
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({checkKernelError(-EIO, "hello", " world");},
+                      EIO, "hello world");
+
+  EXPECT_NO_THROW({checkUnixError(0, "hello", " world");});
+  EXPECT_NO_THROW({checkUnixError(1, "hello", " world");});
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({checkUnixError(-1, "hello", " world");},
+                      ERANGE, "hello world");
+
+  EXPECT_NO_THROW({checkUnixErrorExplicit(0, EIO, "hello", " world");});
+  EXPECT_NO_THROW({checkUnixErrorExplicit(1, EIO, "hello", " world");});
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({checkUnixErrorExplicit(-1, EIO, "hello", " world");},
+                      EIO, "hello world");
+
+  std::shared_ptr<FILE> fp(tmpfile(), fclose);
+  ASSERT_TRUE(fp != nullptr);
+
+  EXPECT_NO_THROW({checkFopenError(fp.get(), "hello", " world");});
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({checkFopenError(nullptr, "hello", " world");},
+                      ERANGE, "hello world");
+
+  EXPECT_NO_THROW({checkFopenErrorExplicit(fp.get(), EIO, "hello", " world");});
+  errno = ERANGE;
+  EXPECT_SYSTEM_ERROR({checkFopenErrorExplicit(nullptr, EIO,
+                                               "hello", " world");},
+                      EIO, "hello world");
+}
+
+}}  // namespaces
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/ExceptionWrapperBenchmark.cpp b/faux-folly/folly/test/ExceptionWrapperBenchmark.cpp
new file mode 100644
index 0000000..475fc89
--- /dev/null
+++ b/faux-folly/folly/test/ExceptionWrapperBenchmark.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2015 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/ExceptionWrapper.h>
+
+#include <gflags/gflags.h>
+#include <atomic>
+#include <exception>
+#include <vector>
+#include <stdexcept>
+#include <thread>
+
+#include <folly/Benchmark.h>
+
+DEFINE_int32(num_threads, 32, "Number of threads to run concurrency "
+                              "benchmarks");
+
+/*
+ * Use case 1: Library wraps errors in either exception_wrapper or
+ * exception_ptr, but user does not care what the exception is after learning
+ * that there is one.
+ */
+BENCHMARK(exception_ptr_create_and_test, iters) {
+  std::runtime_error e("payload");
+  for (size_t i = 0; i < iters; ++i) {
+    auto ep = std::make_exception_ptr(e);
+    assert(ep);
+  }
+}
+
+BENCHMARK_RELATIVE(exception_wrapper_create_and_test, iters) {
+  std::runtime_error e("payload");
+  for (size_t i = 0; i < iters; ++i) {
+    auto ew = folly::make_exception_wrapper<std::runtime_error>(e);
+    assert(ew);
+  }
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(exception_ptr_create_and_test_concurrent, iters) {
+  std::atomic<bool> go(false);
+  std::vector<std::thread> threads;
+  BENCHMARK_SUSPEND {
+    for (int t = 0; t < FLAGS_num_threads; ++t) {
+      threads.emplace_back([&go, iters] {
+        while (!go) { }
+        std::runtime_error e("payload");
+        for (size_t i = 0; i < iters; ++i) {
+          auto ep = std::make_exception_ptr(e);
+          assert(ep);
+        }
+      });
+    }
+  }
+  go.store(true);
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+BENCHMARK_RELATIVE(exception_wrapper_create_and_test_concurrent, iters) {
+  std::atomic<bool> go(false);
+  std::vector<std::thread> threads;
+  BENCHMARK_SUSPEND {
+    for (int t = 0; t < FLAGS_num_threads; ++t) {
+      threads.emplace_back([&go, iters] {
+        while (!go) { }
+        std::runtime_error e("payload");
+        for (size_t i = 0; i < iters; ++i) {
+          auto ew = folly::make_exception_wrapper<std::runtime_error>(e);
+          assert(ew);
+        }
+      });
+    }
+  }
+  go.store(true);
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+BENCHMARK_DRAW_LINE()
+
+/*
+ * Use case 2: Library wraps errors in either exception_wrapper or
+ * exception_ptr, and user wants to handle std::runtime_error. This can be done
+ * either by rehtrowing or with dynamic_cast.
+ */
+BENCHMARK(exception_ptr_create_and_throw, iters) {
+  std::runtime_error e("payload");
+  for (size_t i = 0; i < iters; ++i) {
+    auto ep = std::make_exception_ptr(e);
+    try {
+      std::rethrow_exception(ep);
+      assert(false);
+    } catch (std::runtime_error& e) {
+    }
+  }
+}
+
+BENCHMARK_RELATIVE(exception_wrapper_create_and_throw, iters) {
+  std::runtime_error e("payload");
+  for (size_t i = 0; i < iters; ++i) {
+    auto ew = folly::make_exception_wrapper<std::runtime_error>(e);
+    try {
+      ew.throwException();
+      assert(false);
+    } catch (std::runtime_error& e) {
+    }
+  }
+}
+
+BENCHMARK_RELATIVE(exception_wrapper_create_and_cast, iters) {
+  std::runtime_error e("payload");
+  for (size_t i = 0; i < iters; ++i) {
+    auto ew = folly::make_exception_wrapper<std::runtime_error>(e);
+    assert(ew.is_compatible_with<std::runtime_error>());
+  }
+}
+
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(exception_ptr_create_and_throw_concurrent, iters) {
+  std::atomic<bool> go(false);
+  std::vector<std::thread> threads;
+  BENCHMARK_SUSPEND {
+    for (int t = 0; t < FLAGS_num_threads; ++t) {
+      threads.emplace_back([&go, iters] {
+        while (!go) { }
+        std::runtime_error e("payload");
+        for (size_t i = 0; i < iters; ++i) {
+          auto ep = std::make_exception_ptr(e);
+          try {
+            std::rethrow_exception(ep);
+            assert(false);
+          } catch (std::runtime_error& e) {
+          }
+        }
+      });
+    }
+  }
+  go.store(true);
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+BENCHMARK_RELATIVE(exception_wrapper_create_and_throw_concurrent, iters) {
+  std::atomic<bool> go(false);
+  std::vector<std::thread> threads;
+  BENCHMARK_SUSPEND {
+    for (int t = 0; t < FLAGS_num_threads; ++t) {
+      threads.emplace_back([&go, iters] {
+        while (!go) { }
+        std::runtime_error e("payload");
+        for (size_t i = 0; i < iters; ++i) {
+          auto ew = folly::make_exception_wrapper<std::runtime_error>(e);
+          try {
+            ew.throwException();
+            assert(false);
+          } catch (std::runtime_error& e) {
+          }
+        }
+      });
+    }
+  }
+  go.store(true);
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+BENCHMARK_RELATIVE(exception_wrapper_create_and_cast_concurrent, iters) {
+  std::atomic<bool> go(false);
+  std::vector<std::thread> threads;
+  BENCHMARK_SUSPEND {
+    for (int t = 0; t < FLAGS_num_threads; ++t) {
+      threads.emplace_back([&go, iters] {
+        while (!go) { }
+        std::runtime_error e("payload");
+        for (size_t i = 0; i < iters; ++i) {
+          auto ew = folly::make_exception_wrapper<std::runtime_error>(e);
+          assert(ew.is_compatible_with<std::runtime_error>());
+        }
+      });
+    }
+  }
+  go.store(true);
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+int main(int argc, char *argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
+
+/*
+_bin/folly/test/exception_wrapper_benchmark --bm_min_iters=100000
+============================================================================
+folly/test/ExceptionWrapperBenchmark.cpp        relative  time/iter  iters/s
+============================================================================
+exception_ptr_create_and_test                                2.03us  492.88K
+exception_wrapper_create_and_test               2542.59%    79.80ns   12.53M
+----------------------------------------------------------------------------
+exception_ptr_create_and_test_concurrent                   162.39us    6.16K
+exception_wrapper_create_and_test_concurrent    95847.91%   169.43ns    5.90M
+----------------------------------------------------------------------------
+exception_ptr_create_and_throw                               4.24us  236.06K
+exception_wrapper_create_and_throw               141.15%     3.00us  333.20K
+exception_wrapper_create_and_cast               5321.54%    79.61ns   12.56M
+----------------------------------------------------------------------------
+exception_ptr_create_and_throw_concurrent                  330.88us    3.02K
+exception_wrapper_create_and_throw_concurrent    143.66%   230.32us    4.34K
+exception_wrapper_create_and_cast_concurrent    194828.54%   169.83ns    5.89M
+============================================================================
+*/
diff --git a/faux-folly/folly/test/ExceptionWrapperTest.cpp b/faux-folly/folly/test/ExceptionWrapperTest.cpp
new file mode 100644
index 0000000..79660da
--- /dev/null
+++ b/faux-folly/folly/test/ExceptionWrapperTest.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+#include <stdexcept>
+#include <folly/ExceptionWrapper.h>
+#include <folly/Conv.h>
+
+using namespace folly;
+
+// Tests that when we call throwException, the proper type is thrown (derived)
+TEST(ExceptionWrapper, throw_test) {
+  std::runtime_error e("payload");
+  auto ew = make_exception_wrapper<std::runtime_error>(e);
+
+  std::vector<exception_wrapper> container;
+  container.push_back(ew);
+
+  try {
+    container[0].throwException();
+  } catch (std::runtime_error& e) {
+    std::string expected = "payload";
+    std::string actual = e.what();
+    EXPECT_EQ(expected, actual);
+  }
+}
+
+TEST(ExceptionWrapper, members) {
+  auto ew = exception_wrapper();
+  EXPECT_FALSE(bool(ew));
+  EXPECT_EQ(ew.what(), "");
+  EXPECT_EQ(ew.class_name(), "");
+  ew = make_exception_wrapper<std::runtime_error>("payload");
+  EXPECT_TRUE(bool(ew));
+  EXPECT_EQ(ew.what(), "std::runtime_error: payload");
+  EXPECT_EQ(ew.class_name(), "std::runtime_error");
+}
+
+TEST(ExceptionWrapper, equals) {
+  std::runtime_error e("payload");
+  auto ew1 = make_exception_wrapper<std::runtime_error>(e);
+  auto ew2 = ew1;
+  EXPECT_EQ(ew1, ew2);
+
+  auto ew3 = try_and_catch<std::exception>([&]() {
+    throw std::runtime_error("payload");
+  });
+  auto ew4 = try_and_catch<std::exception>([&]() {
+    ew3.throwException();
+  });
+  EXPECT_EQ(ew3, ew4);
+}
+
+TEST(ExceptionWrapper, not_equals) {
+  std::runtime_error e1("payload");
+  std::runtime_error e2("payload");
+  auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
+  auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
+  EXPECT_NE(ew1, ew2);
+
+  auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
+  auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
+  EXPECT_NE(ew3, ew4);
+
+  auto ew5 = try_and_catch<std::exception>([&]() {
+    throw e1;
+  });
+  auto ew6 = try_and_catch<std::exception>([&]() {
+    throw e1;
+  });
+  EXPECT_NE(ew5, ew6);
+}
+
+TEST(ExceptionWrapper, try_and_catch_test) {
+  std::string expected = "payload";
+
+  // Catch rightmost matching exception type
+  exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
+    [=]() {
+      throw std::runtime_error(expected);
+    });
+  EXPECT_TRUE(bool(ew));
+  EXPECT_TRUE(ew.getCopied());
+  EXPECT_EQ(ew.what(), "std::runtime_error: payload");
+  EXPECT_EQ(ew.class_name(), "std::runtime_error");
+  auto rep = ew.is_compatible_with<std::runtime_error>();
+  EXPECT_TRUE(rep);
+
+  // Changing order is like catching in wrong order. Beware of this in your
+  // code.
+  auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
+    throw std::runtime_error(expected);
+  });
+  EXPECT_TRUE(bool(ew2));
+  // We are catching a std::exception, not std::runtime_error.
+  EXPECT_FALSE(ew2.getCopied());
+  // But, we can still get the actual type if we want it.
+  rep = ew2.is_compatible_with<std::runtime_error>();
+  EXPECT_TRUE(rep);
+
+  // Catches even if not rightmost.
+  auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
+    throw std::exception();
+  });
+  EXPECT_TRUE(bool(ew3));
+  EXPECT_EQ(ew3.what(), "std::exception: std::exception");
+  EXPECT_EQ(ew3.class_name(), "std::exception");
+  rep = ew3.is_compatible_with<std::runtime_error>();
+  EXPECT_FALSE(rep);
+
+  // If does not catch, throws.
+  EXPECT_THROW(
+    try_and_catch<std::runtime_error>([]() {
+      throw std::exception();
+    }),
+    std::exception);
+}
+
+class AbstractIntException : public std::exception {
+public:
+  virtual int getInt() const = 0;
+};
+
+class IntException : public AbstractIntException {
+public:
+  explicit IntException(int i)
+    : i_(i) {}
+
+  int getInt() const override { return i_; }
+  const char* what() const noexcept override {
+    what_ = folly::to<std::string>("int == ", i_);
+    return what_.c_str();
+  }
+
+private:
+  int i_;
+  mutable std::string what_;
+};
+
+TEST(ExceptionWrapper, with_exception_test) {
+  int expected = 23;
+
+  // This works, and doesn't slice.
+  exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
+    [=]() {
+      throw IntException(expected);
+    });
+  EXPECT_TRUE(bool(ew));
+  EXPECT_EQ(ew.what(), "IntException: int == 23");
+  EXPECT_EQ(ew.class_name(), "IntException");
+  ew.with_exception<IntException>([&](const IntException& ie) {
+      EXPECT_EQ(ie.getInt(), expected);
+    });
+
+  // I can try_and_catch a non-copyable base class.  This will use
+  // std::exception_ptr internally.
+  exception_wrapper ew2 = try_and_catch<AbstractIntException>(
+    [=]() {
+      throw IntException(expected);
+    });
+  EXPECT_TRUE(bool(ew2));
+  EXPECT_EQ(ew2.what(), "IntException: int == 23");
+  EXPECT_EQ(ew2.class_name(), "IntException");
+  ew2.with_exception<AbstractIntException>([&](AbstractIntException& ie) {
+      EXPECT_EQ(ie.getInt(), expected);
+#if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunevaluated-expression"
+#endif
+      EXPECT_EQ(typeid(ie), typeid(IntException));
+#if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6)
+# pragma clang diagnostic pop
+#endif
+    });
+
+  // Test with const this.  If this compiles and does not crash due to
+  // infinite loop when it runs, it succeeds.
+  const exception_wrapper& cew = ew;
+  cew.with_exception<IntException>([&](const IntException& ie) {
+      SUCCEED();
+    });
+
+  // This won't even compile.  You can't use a function which takes a
+  // non-const reference with a const exception_wrapper.
+/*
+  cew.with_exception<IntException>([&](IntException& ie) {
+      SUCCEED();
+    });
+*/
+}
+
+TEST(ExceptionWrapper, non_std_exception_test) {
+  int expected = 17;
+
+  exception_wrapper ew = try_and_catch<std::exception, int>(
+    [=]() {
+      throw expected;
+    });
+  EXPECT_TRUE(bool(ew));
+  EXPECT_FALSE(ew.is_compatible_with<std::exception>());
+  EXPECT_EQ(ew.what(), "int");
+  EXPECT_EQ(ew.class_name(), "int");
+  // non-std::exception types are supported, but the only way to
+  // access their value is to explicity rethrow and catch it.
+  try {
+    ew.throwException();
+  } catch /* nolint */ (int& i) {
+    EXPECT_EQ(i, expected);
+  }
+}
+
+
+TEST(ExceptionWrapper, exceptionStr) {
+  auto ew = make_exception_wrapper<std::runtime_error>("argh");
+  EXPECT_EQ("std::runtime_error: argh", exceptionStr(ew));
+}
+
+namespace {
+class TestException : public std::exception { };
+void testEW(const exception_wrapper& ew) {
+  EXPECT_THROW(ew.throwException(), TestException);
+}
+}  // namespace
+
+TEST(ExceptionWrapper, implicitConstruction) {
+  // Try with both lvalue and rvalue references
+  TestException e;
+  testEW(e);
+  testEW(TestException());
+}
diff --git a/faux-folly/folly/test/FBStringBenchmark.cpp b/faux-folly/folly/test/FBStringBenchmark.cpp
new file mode 100644
index 0000000..8cb9b41
--- /dev/null
+++ b/faux-folly/folly/test/FBStringBenchmark.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 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.
+ */
+
+//
+// Author: andrei.alexandrescu@fb.com
+
+#include <folly/FBString.h>
+
+#include <cstdlib>
+
+#include <list>
+#include <fstream>
+#include <boost/algorithm/string.hpp>
+#include <boost/random.hpp>
+
+#include <gflags/gflags.h>
+
+#include <folly/Foreach.h>
+#include <folly/Random.h>
+#include <folly/Benchmark.h>
+
+using namespace std;
+using namespace folly;
+
+static const int seed = folly::randomNumberSeed();
+typedef boost::mt19937 RandomT;
+static RandomT rng(seed);
+static const size_t maxString = 100;
+static const bool avoidAliasing = true;
+
+template <class Integral1, class Integral2>
+Integral2 random(Integral1 low, Integral2 up) {
+  boost::uniform_int<> range(low, up);
+  return range(rng);
+}
+
+template <class String>
+void randomString(String* toFill, unsigned int maxSize = 1000) {
+  assert(toFill);
+  toFill->resize(random(0, maxSize));
+  FOR_EACH (i, *toFill) {
+    *i = random('a', 'z');
+  }
+}
+
+template <class String>
+void randomBinaryString(String* toFill, unsigned int maxSize = 1000) {
+  assert(toFill);
+  toFill->resize(random(0, maxSize));
+  FOR_EACH (i, *toFill) {
+    *i = random('0', '1');
+  }
+}
+
+template <class String, class Integral>
+void Num2String(String& str, Integral n) {
+  str.resize(30, '\0');
+  sprintf(&str[0], "%lu", static_cast<unsigned long>(n));
+  str.resize(strlen(str.c_str()));
+}
+
+std::list<char> RandomList(unsigned int maxSize) {
+  std::list<char> lst(random(0u, maxSize));
+  std::list<char>::iterator i = lst.begin();
+  for (; i != lst.end(); ++i) {
+    *i = random('a', 'z');
+ }
+  return lst;
+}
+
+#define CONCAT(A, B) CONCAT_HELPER(A, B)
+#define CONCAT_HELPER(A, B) A##B
+#define BENCHFUN(F) CONCAT(CONCAT(BM_, F), CONCAT(_, STRING))
+
+#define STRING string
+#include <folly/test/FBStringTestBenchmarks.cpp.h> // nolint
+#undef STRING
+#define STRING fbstring
+#include <folly/test/FBStringTestBenchmarks.cpp.h> // nolint
+#undef STRING
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
+
+/*
+malloc
+
+BENCHFUN(defaultCtor)                  100000  1.426 s   14.26 us  68.47 k
+BM_copyCtor_string/32k                 100000  63.48 ms  634.8 ns  1.502 M
+BM_ctorFromArray_string/32k            100000  303.3 ms  3.033 us  321.9 k
+BM_ctorFromChar_string/1M              100000  9.915 ms  99.15 ns  9.619 M
+BM_assignmentOp_string/256             100000  69.09 ms  690.9 ns   1.38 M
+BENCHFUN(assignmentFill)               100000  1.775 ms  17.75 ns  53.73 M
+BM_resize_string/512k                  100000  1.667 s   16.67 us  58.58 k
+BM_findSuccessful_string/512k          100000  287.3 ms  2.873 us  339.9 k
+BM_findUnsuccessful_string/512k        100000  320.3 ms  3.203 us  304.9 k
+BM_replace_string/256                  100000  69.68 ms  696.8 ns  1.369 M
+BM_push_back_string/1k                 100000  433.1 ms  4.331 us  225.5 k
+
+BENCHFUN(defaultCtor)                  100000  1.086 s   10.86 us  89.91 k
+BM_copyCtor_fbstring/32k               100000  4.218 ms  42.18 ns  22.61 M
+BM_ctorFromArray_fbstring/32k          100000  145.2 ms  1.452 us  672.7 k
+BM_ctorFromChar_fbstring/1M            100000   9.21 ms   92.1 ns  10.35 M
+BM_assignmentOp_fbstring/256           100000  61.95 ms  619.5 ns   1.54 M
+BENCHFUN(assignmentFill)               100000   1.41 ms   14.1 ns  67.64 M
+BM_resize_fbstring/512k                100000  1.668 s   16.68 us  58.56 k
+BM_findSuccessful_fbstring/512k        100000   20.6 ms    206 ns  4.629 M
+BM_findUnsuccessful_fbstring/512k      100000  141.3 ms  1.413 us  691.1 k
+BM_replace_fbstring/256                100000  77.12 ms  771.2 ns  1.237 M
+BM_push_back_fbstring/1k               100000  1.745 s   17.45 us  55.95 k
+
+jemalloc
+
+BENCHFUN(defaultCtor)                  100000  1.426 s   14.26 us   68.5 k
+BM_copyCtor_string/32k                 100000  275.7 ms  2.757 us  354.2 k
+BM_ctorFromArray_string/32k            100000    270 ms    2.7 us  361.7 k
+BM_ctorFromChar_string/1M              100000  10.36 ms  103.6 ns  9.206 M
+BM_assignmentOp_string/256             100000  70.44 ms  704.3 ns  1.354 M
+BENCHFUN(assignmentFill)               100000  1.766 ms  17.66 ns     54 M
+BM_resize_string/512k                  100000  1.675 s   16.75 us  58.29 k
+BM_findSuccessful_string/512k          100000  90.89 ms  908.9 ns  1.049 M
+BM_findUnsuccessful_string/512k        100000  315.1 ms  3.151 us  309.9 k
+BM_replace_string/256                  100000  71.14 ms  711.4 ns  1.341 M
+BM_push_back_string/1k                 100000  425.1 ms  4.251 us  229.7 k
+
+BENCHFUN(defaultCtor)                  100000  1.082 s   10.82 us  90.23 k
+BM_copyCtor_fbstring/32k               100000  4.213 ms  42.13 ns  22.64 M
+BM_ctorFromArray_fbstring/32k          100000  113.2 ms  1.132 us    863 k
+BM_ctorFromChar_fbstring/1M            100000  9.162 ms  91.62 ns  10.41 M
+BM_assignmentOp_fbstring/256           100000  61.34 ms  613.4 ns  1.555 M
+BENCHFUN(assignmentFill)               100000  1.408 ms  14.08 ns  67.73 M
+BM_resize_fbstring/512k                100000  1.671 s   16.71 us  58.43 k
+BM_findSuccessful_fbstring/512k        100000  8.723 ms  87.23 ns  10.93 M
+BM_findUnsuccessful_fbstring/512k      100000  141.3 ms  1.413 us  691.2 k
+BM_replace_fbstring/256                100000  77.83 ms  778.3 ns  1.225 M
+BM_push_back_fbstring/1k               100000  1.744 s   17.44 us  55.99 k
+*/
diff --git a/faux-folly/folly/test/FBStringTest.cpp b/faux-folly/folly/test/FBStringTest.cpp
new file mode 100644
index 0000000..4ad66db
--- /dev/null
+++ b/faux-folly/folly/test/FBStringTest.cpp
@@ -0,0 +1,1368 @@
+/*
+ * Copyright 2015 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.
+ */
+
+//
+// Author: andrei.alexandrescu@fb.com
+
+#include <folly/FBString.h>
+
+#include <cstdlib>
+
+#include <list>
+#include <fstream>
+#include <iomanip>
+#include <boost/algorithm/string.hpp>
+#include <boost/random.hpp>
+#include <gtest/gtest.h>
+
+#include <gflags/gflags.h>
+
+#include <folly/Foreach.h>
+#include <folly/Portability.h>
+#include <folly/Random.h>
+#include <folly/Conv.h>
+
+using namespace std;
+using namespace folly;
+
+static const int seed = folly::randomNumberSeed();
+typedef boost::mt19937 RandomT;
+static RandomT rng(seed);
+static const size_t maxString = 100;
+static const bool avoidAliasing = true;
+
+template <class Integral1, class Integral2>
+Integral2 random(Integral1 low, Integral2 up) {
+  boost::uniform_int<> range(low, up);
+  return range(rng);
+}
+
+template <class String>
+void randomString(String* toFill, unsigned int maxSize = 1000) {
+  assert(toFill);
+  toFill->resize(random(0, maxSize));
+  FOR_EACH (i, *toFill) {
+    *i = random('a', 'z');
+  }
+}
+
+template <class String, class Integral>
+void Num2String(String& str, Integral n) {
+
+  std::string tmp = folly::to<std::string>(n);
+  str = String(tmp.begin(), tmp.end());
+}
+
+std::list<char> RandomList(unsigned int maxSize) {
+  std::list<char> lst(random(0u, maxSize));
+  std::list<char>::iterator i = lst.begin();
+  for (; i != lst.end(); ++i) {
+    *i = random('a', 'z');
+ }
+  return lst;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests begin here
+////////////////////////////////////////////////////////////////////////////////
+
+template <class String> void clause11_21_4_2_a(String & test) {
+  test.String::~String();
+  new(&test) String();
+}
+template <class String> void clause11_21_4_2_b(String & test) {
+  String test2(test);
+  assert(test2 == test);
+}
+template <class String> void clause11_21_4_2_c(String & test) {
+  // Test move constructor. There is a more specialized test, see
+  // TEST(FBString, testMoveCtor)
+  String donor(test);
+  String test2(std::move(donor));
+  EXPECT_EQ(test2, test);
+  // Technically not required, but all implementations that actually
+  // support move will move large strings. Make a guess for 128 as the
+  // maximum small string optimization that's reasonable.
+  EXPECT_LE(donor.size(), 128);
+}
+template <class String> void clause11_21_4_2_d(String & test) {
+  // Copy constructor with position and length
+  const size_t pos = random(0, test.size());
+  String s(test, pos, random(0, 9)
+           ? random(0, (size_t)(test.size() - pos))
+           : String::npos); // test for npos, too, in 10% of the cases
+  test = s;
+}
+template <class String> void clause11_21_4_2_e(String & test) {
+  // Constructor from char*, size_t
+  const size_t
+    pos = random(0, test.size()),
+    n = random(0, test.size() - pos);
+  String before(test.data(), test.size());
+  String s(test.c_str() + pos, n);
+  String after(test.data(), test.size());
+  EXPECT_EQ(before, after);
+  test.swap(s);
+}
+template <class String> void clause11_21_4_2_f(String & test) {
+  // Constructor from char*
+  const size_t
+    pos = random(0, test.size()),
+    n = random(0, test.size() - pos);
+  String before(test.data(), test.size());
+  String s(test.c_str() + pos);
+  String after(test.data(), test.size());
+  EXPECT_EQ(before, after);
+  test.swap(s);
+}
+template <class String> void clause11_21_4_2_g(String & test) {
+  // Constructor from size_t, char
+  const size_t n = random(0, test.size());
+  const auto c = test.front();
+  test = String(n, c);
+}
+template <class String> void clause11_21_4_2_h(String & test) {
+  // Constructors from various iterator pairs
+  // Constructor from char*, char*
+  String s1(test.begin(), test.end());
+  EXPECT_EQ(test, s1);
+  String s2(test.data(), test.data() + test.size());
+  EXPECT_EQ(test, s2);
+  // Constructor from other iterators
+  std::list<char> lst;
+  for (auto c : test) lst.push_back(c);
+  String s3(lst.begin(), lst.end());
+  EXPECT_EQ(test, s3);
+  // Constructor from wchar_t iterators
+  std::list<wchar_t> lst1;
+  for (auto c : test) lst1.push_back(c);
+  String s4(lst1.begin(), lst1.end());
+  EXPECT_EQ(test, s4);
+  // Constructor from wchar_t pointers
+  wchar_t t[20];
+  t[0] = 'a';
+  t[1] = 'b';
+  fbstring s5(t, t + 2);;
+  EXPECT_EQ("ab", s5);
+}
+template <class String> void clause11_21_4_2_i(String & test) {
+  // From initializer_list<char>
+  std::initializer_list<typename String::value_type>
+    il = { 'h', 'e', 'l', 'l', 'o' };
+  String s(il);
+  test.swap(s);
+}
+template <class String> void clause11_21_4_2_j(String & test) {
+  // Assignment from const String&
+  auto size = random(0, 2000);
+  String s(size, '\0');
+  EXPECT_EQ(s.size(), size);
+  FOR_EACH_RANGE (i, 0, s.size()) {
+    s[i] = random('a', 'z');
+  }
+  test = s;
+}
+template <class String> void clause11_21_4_2_k(String & test) {
+  // Assignment from String&&
+  auto size = random(0, 2000);
+  String s(size, '\0');
+  EXPECT_EQ(s.size(), size);
+  FOR_EACH_RANGE (i, 0, s.size()) {
+    s[i] = random('a', 'z');
+  }
+  test = std::move(s);
+  if (typeid(String) == typeid(fbstring)) {
+    EXPECT_LE(s.size(), 128);
+  }
+}
+template <class String> void clause11_21_4_2_l(String & test) {
+  // Assignment from char*
+  String s(random(0, 1000), '\0');
+  size_t i = 0;
+  for (; i != s.size(); ++i) {
+    s[i] = random('a', 'z');
+  }
+  test = s.c_str();
+}
+template <class String> void clause11_21_4_2_lprime(String & test) {
+  // Aliased assign
+  const size_t pos = random(0, test.size());
+  if (avoidAliasing) {
+    test = String(test.c_str() + pos);
+  } else {
+    test = test.c_str() + pos;
+  }
+}
+template <class String> void clause11_21_4_2_m(String & test) {
+  // Assignment from char
+  test = random('a', 'z');
+}
+template <class String> void clause11_21_4_2_n(String & test) {
+  // Assignment from initializer_list<char>
+  initializer_list<typename String::value_type>
+    il = { 'h', 'e', 'l', 'l', 'o' };
+  test = il;
+}
+
+template <class String> void clause11_21_4_3(String & test) {
+  // Iterators. The code below should leave test unchanged
+  EXPECT_EQ(test.size(), test.end() - test.begin());
+  EXPECT_EQ(test.size(), test.rend() - test.rbegin());
+  EXPECT_EQ(test.size(), test.cend() - test.cbegin());
+  EXPECT_EQ(test.size(), test.crend() - test.crbegin());
+
+  auto s = test.size();
+  test.resize(test.end() - test.begin());
+  EXPECT_EQ(s, test.size());
+  test.resize(test.rend() - test.rbegin());
+  EXPECT_EQ(s, test.size());
+}
+
+template <class String> void clause11_21_4_4(String & test) {
+  // exercise capacity, size, max_size
+  EXPECT_EQ(test.size(), test.length());
+  EXPECT_LE(test.size(), test.max_size());
+  EXPECT_LE(test.capacity(), test.max_size());
+  EXPECT_LE(test.size(), test.capacity());
+
+  // exercise shrink_to_fit. Nonbinding request so we can't really do
+  // much beyond calling it.
+  auto copy = test;
+  copy.reserve(copy.capacity() * 3);
+  copy.shrink_to_fit();
+  EXPECT_EQ(copy, test);
+
+  // exercise empty
+  string empty("empty");
+  string notempty("not empty");
+  if (test.empty()) test = String(empty.begin(), empty.end());
+  else test = String(notempty.begin(), notempty.end());
+}
+
+template <class String> void clause11_21_4_5(String & test) {
+  // exercise element access
+  if (!test.empty()) {
+    EXPECT_EQ(test[0], test.front());
+    EXPECT_EQ(test[test.size() - 1], test.back());
+    auto const i = random(0, test.size() - 1);
+    EXPECT_EQ(test[i], test.at(i));
+    test = test[i];
+  }
+}
+
+template <class String> void clause11_21_4_6_1(String & test) {
+  // 21.3.5 modifiers (+=)
+  String test1;
+  randomString(&test1);
+  assert(test1.size() == char_traits
+      <typename String::value_type>::length(test1.c_str()));
+  auto len = test.size();
+  test += test1;
+  EXPECT_EQ(test.size(), test1.size() + len);
+  FOR_EACH_RANGE (i, 0, test1.size()) {
+    EXPECT_EQ(test[len + i], test1[i]);
+  }
+  // aliasing modifiers
+  String test2 = test;
+  auto dt = test2.data();
+  auto sz = test.c_str();
+  len = test.size();
+  EXPECT_EQ(memcmp(sz, dt, len), 0);
+  String copy(test.data(), test.size());
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str()), len);
+  test += test;
+  //test.append(test);
+  EXPECT_EQ(test.size(), 2 * len);
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str()), 2 * len);
+  FOR_EACH_RANGE (i, 0, len) {
+    EXPECT_EQ(test[i], copy[i]);
+    EXPECT_EQ(test[i], test[len + i]);
+  }
+  len = test.size();
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str()), len);
+  // more aliasing
+  auto const pos = random(0, test.size());
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str() + pos), len - pos);
+  if (avoidAliasing) {
+    String addMe(test.c_str() + pos);
+    EXPECT_EQ(addMe.size(), len - pos);
+    test += addMe;
+  } else {
+    test += test.c_str() + pos;
+  }
+  EXPECT_EQ(test.size(), 2 * len - pos);
+  // single char
+  len = test.size();
+  test += random('a', 'z');
+  EXPECT_EQ(test.size(), len + 1);
+  // initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  test += il;
+}
+
+template <class String> void clause11_21_4_6_2(String & test) {
+  // 21.3.5 modifiers (append, push_back)
+  String s;
+
+  // Test with a small string first
+  char c = random('a', 'z');
+  s.push_back(c);
+  EXPECT_EQ(s[s.size() - 1], c);
+  EXPECT_EQ(s.size(), 1);
+  s.resize(s.size() - 1);
+
+  randomString(&s, maxString);
+  test.append(s);
+  randomString(&s, maxString);
+  test.append(s, random(0, s.size()), random(0, maxString));
+  randomString(&s, maxString);
+  test.append(s.c_str(), random(0, s.size()));
+  randomString(&s, maxString);
+  test.append(s.c_str());
+  test.append(random(0, maxString), random('a', 'z'));
+  std::list<char> lst(RandomList(maxString));
+  test.append(lst.begin(), lst.end());
+  c = random('a', 'z');
+  test.push_back(c);
+  EXPECT_EQ(test[test.size() - 1], c);
+  // initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  test.append(il);
+}
+
+template <class String> void clause11_21_4_6_3_a(String & test) {
+  // assign
+  String s;
+  randomString(&s);
+  test.assign(s);
+  EXPECT_EQ(test, s);
+  // move assign
+  test.assign(std::move(s));
+  if (typeid(String) == typeid(fbstring)) {
+    EXPECT_LE(s.size(), 128);
+  }
+}
+
+template <class String> void clause11_21_4_6_3_b(String & test) {
+  // assign
+  String s;
+  randomString(&s, maxString);
+  test.assign(s, random(0, s.size()), random(0, maxString));
+}
+
+template <class String> void clause11_21_4_6_3_c(String & test) {
+  // assign
+  String s;
+  randomString(&s, maxString);
+  test.assign(s.c_str(), random(0, s.size()));
+}
+
+template <class String> void clause11_21_4_6_3_d(String & test) {
+  // assign
+  String s;
+  randomString(&s, maxString);
+  test.assign(s.c_str());
+}
+
+template <class String> void clause11_21_4_6_3_e(String & test) {
+  // assign
+  String s;
+  randomString(&s, maxString);
+  test.assign(random(0, maxString), random('a', 'z'));
+}
+
+template <class String> void clause11_21_4_6_3_f(String & test) {
+  // assign from bidirectional iterator
+  std::list<char> lst(RandomList(maxString));
+  test.assign(lst.begin(), lst.end());
+}
+
+template <class String> void clause11_21_4_6_3_g(String & test) {
+  // assign from aliased source
+  test.assign(test);
+}
+
+template <class String> void clause11_21_4_6_3_h(String & test) {
+  // assign from aliased source
+  test.assign(test, random(0, test.size()), random(0, maxString));
+}
+
+template <class String> void clause11_21_4_6_3_i(String & test) {
+  // assign from aliased source
+  test.assign(test.c_str(), random(0, test.size()));
+}
+
+template <class String> void clause11_21_4_6_3_j(String & test) {
+  // assign from aliased source
+  test.assign(test.c_str());
+}
+
+template <class String> void clause11_21_4_6_3_k(String & test) {
+  // assign from initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  test.assign(il);
+}
+
+template <class String> void clause11_21_4_6_4(String & test) {
+  // insert
+  String s;
+  randomString(&s, maxString);
+  test.insert(random(0, test.size()), s);
+  randomString(&s, maxString);
+  test.insert(random(0, test.size()),
+              s, random(0, s.size()),
+              random(0, maxString));
+  randomString(&s, maxString);
+  test.insert(random(0, test.size()),
+              s.c_str(), random(0, s.size()));
+  randomString(&s, maxString);
+  test.insert(random(0, test.size()), s.c_str());
+  test.insert(random(0, test.size()),
+              random(0, maxString), random('a', 'z'));
+  typename String::size_type pos = random(0, test.size());
+  typename String::iterator res =
+    test.insert(test.begin() + pos, random('a', 'z'));
+  EXPECT_EQ(res - test.begin(), pos);
+  std::list<char> lst(RandomList(maxString));
+  pos = random(0, test.size());
+  // Uncomment below to see a bug in gcc
+  /*res = */test.insert(test.begin() + pos, lst.begin(), lst.end());
+  // insert from initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  pos = random(0, test.size());
+  // Uncomment below to see a bug in gcc
+  /*res = */test.insert(test.begin() + pos, il);
+
+  // Test with actual input iterators
+  stringstream ss;
+  ss << "hello cruel world";
+  auto i = istream_iterator<char>(ss);
+  test.insert(test.begin(), i, istream_iterator<char>());
+}
+
+template <class String> void clause11_21_4_6_5(String & test) {
+  // erase and pop_back
+  if (!test.empty()) {
+    test.erase(random(0, test.size()), random(0, maxString));
+  }
+  if (!test.empty()) {
+    // TODO: is erase(end()) allowed?
+    test.erase(test.begin() + random(0, test.size() - 1));
+  }
+  if (!test.empty()) {
+    auto const i = test.begin() + random(0, test.size());
+    if (i != test.end()) {
+      test.erase(i, i + random(0, size_t(test.end() - i)));
+    }
+  }
+  if (!test.empty()) {
+    // Can't test pop_back with std::string, doesn't support it yet.
+    //test.pop_back();
+  }
+}
+
+template <class String> void clause11_21_4_6_6(String & test) {
+  auto pos = random(0, test.size());
+  if (avoidAliasing) {
+    test.replace(pos, random(0, test.size() - pos),
+                 String(test));
+  } else {
+    test.replace(pos, random(0, test.size() - pos), test);
+  }
+  pos = random(0, test.size());
+  String s;
+  randomString(&s, maxString);
+  test.replace(pos, pos + random(0, test.size() - pos), s);
+  auto pos1 = random(0, test.size());
+  auto pos2 = random(0, test.size());
+  if (avoidAliasing) {
+    test.replace(pos1, pos1 + random(0, test.size() - pos1),
+                 String(test),
+                 pos2, pos2 + random(0, test.size() - pos2));
+  } else {
+    test.replace(pos1, pos1 + random(0, test.size() - pos1),
+                 test, pos2, pos2 + random(0, test.size() - pos2));
+  }
+  pos1 = random(0, test.size());
+  String str;
+  randomString(&str, maxString);
+  pos2 = random(0, str.size());
+  test.replace(pos1, pos1 + random(0, test.size() - pos1),
+               str, pos2, pos2 + random(0, str.size() - pos2));
+  pos = random(0, test.size());
+  if (avoidAliasing) {
+    test.replace(pos, random(0, test.size() - pos),
+                 String(test).c_str(), test.size());
+  } else {
+    test.replace(pos, random(0, test.size() - pos),
+                 test.c_str(), test.size());
+  }
+  pos = random(0, test.size());
+  randomString(&str, maxString);
+  test.replace(pos, pos + random(0, test.size() - pos),
+               str.c_str(), str.size());
+  pos = random(0, test.size());
+  randomString(&str, maxString);
+  test.replace(pos, pos + random(0, test.size() - pos),
+               str.c_str());
+  pos = random(0, test.size());
+  test.replace(pos, random(0, test.size() - pos),
+               random(0, maxString), random('a', 'z'));
+  pos = random(0, test.size());
+  if (avoidAliasing) {
+    auto newString = String(test);
+    test.replace(
+      test.begin() + pos,
+      test.begin() + pos + random(0, test.size() - pos),
+      newString);
+  } else {
+    test.replace(
+      test.begin() + pos,
+      test.begin() + pos + random(0, test.size() - pos),
+      test);
+  }
+  pos = random(0, test.size());
+  if (avoidAliasing) {
+    auto newString = String(test);
+    test.replace(
+      test.begin() + pos,
+      test.begin() + pos + random(0, test.size() - pos),
+      newString.c_str(),
+      test.size() - random(0, test.size()));
+  } else {
+    test.replace(
+      test.begin() + pos,
+      test.begin() + pos + random(0, test.size() - pos),
+      test.c_str(),
+      test.size() - random(0, test.size()));
+  }
+  pos = random(0, test.size());
+  auto const n = random(0, test.size() - pos);
+  typename String::iterator b = test.begin();
+  String str1;
+  randomString(&str1, maxString);
+  const String & str3 = str1;
+  const typename String::value_type* ss = str3.c_str();
+  test.replace(
+    b + pos,
+    b + pos + n,
+    ss);
+  pos = random(0, test.size());
+  test.replace(
+    test.begin() + pos,
+    test.begin() + pos + random(0, test.size() - pos),
+    random(0, maxString), random('a', 'z'));
+}
+
+template <class String> void clause11_21_4_6_7(String & test) {
+  std::vector<typename String::value_type>
+    vec(random(0, maxString));
+  test.copy(
+    &vec[0],
+    vec.size(),
+    random(0, test.size()));
+}
+
+template <class String> void clause11_21_4_6_8(String & test) {
+  String s;
+  randomString(&s, maxString);
+  s.swap(test);
+}
+
+template <class String> void clause11_21_4_7_1(String & test) {
+  // 21.3.6 string operations
+  // exercise c_str() and data()
+  assert(test.c_str() == test.data());
+  // exercise get_allocator()
+  String s;
+  randomString(&s, maxString);
+  assert(test.get_allocator() == s.get_allocator());
+}
+
+template <class String> void clause11_21_4_7_2_a(String & test) {
+  String str = test.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str, random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_a1(String & test) {
+  String str = String(test).substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str, random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_a2(String & test) {
+  auto const& cTest = test;
+  String str = cTest.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str, random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_b(String & test) {
+  auto from = random(0, test.size());
+  auto length = random(0, test.size() - from);
+  String str = test.substr(from, length);
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size()),
+                             random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_2_b1(String & test) {
+  auto from = random(0, test.size());
+  auto length = random(0, test.size() - from);
+  String str = String(test).substr(from, length);
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size()),
+                             random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_2_b2(String & test) {
+  auto from = random(0, test.size());
+  auto length = random(0, test.size() - from);
+  const auto& cTest = test;
+  String str = cTest.substr(from, length);
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size()),
+                             random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_2_c(String & test) {
+  String str = test.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_c1(String & test) {
+  String str = String(test).substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_c2(String & test) {
+  const auto& cTest = test;
+  String str = cTest.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_d(String & test) {
+  Num2String(test, test.find(
+               random('a', 'z'),
+               random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_3_a(String & test) {
+  String str = test.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.rfind(str, random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_3_b(String & test) {
+  String str = test.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.rfind(str.c_str(),
+                              random(0, test.size()),
+                              random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_3_c(String & test) {
+  String str = test.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.rfind(str.c_str(),
+                              random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_3_d(String & test) {
+  Num2String(test, test.rfind(
+               random('a', 'z'),
+               random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_4_a(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_first_of(str,
+                                      random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_4_b(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_first_of(str.c_str(),
+                                      random(0, test.size()),
+                                      random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_4_c(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_first_of(str.c_str(),
+                                      random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_4_d(String & test) {
+  Num2String(test, test.find_first_of(
+               random('a', 'z'),
+               random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_5_a(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_last_of(str,
+                                     random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_5_b(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_last_of(str.c_str(),
+                                     random(0, test.size()),
+                                     random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_5_c(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_last_of(str.c_str(),
+                                     random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_5_d(String & test) {
+  Num2String(test, test.find_last_of(
+               random('a', 'z'),
+               random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_6_a(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_first_not_of(str,
+                                          random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_6_b(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_first_not_of(str.c_str(),
+                                          random(0, test.size()),
+                                          random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_6_c(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_first_not_of(str.c_str(),
+                                          random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_6_d(String & test) {
+  Num2String(test, test.find_first_not_of(
+               random('a', 'z'),
+               random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_7_a(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_last_not_of(str,
+                                         random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_7_b(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_last_not_of(str.c_str(),
+                                         random(0, test.size()),
+                                         random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_7_c(String & test) {
+  String str;
+  randomString(&str, maxString);
+  Num2String(test, test.find_last_not_of(str.c_str(),
+                                         random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_7_d(String & test) {
+  Num2String(test, test.find_last_not_of(
+               random('a', 'z'),
+               random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_8(String & test) {
+  test = test.substr(random(0, test.size()), random(0, test.size()));
+}
+
+template <class String> void clause11_21_4_7_9_a(String & test) {
+  String s;
+  randomString(&s, maxString);
+  int tristate = test.compare(s);
+  if (tristate > 0) tristate = 1;
+  else if (tristate < 0) tristate = 2;
+  Num2String(test, tristate);
+}
+
+template <class String> void clause11_21_4_7_9_b(String & test) {
+  String s;
+  randomString(&s, maxString);
+  int tristate = test.compare(
+    random(0, test.size()),
+    random(0, test.size()),
+    s);
+  if (tristate > 0) tristate = 1;
+  else if (tristate < 0) tristate = 2;
+  Num2String(test, tristate);
+}
+
+template <class String> void clause11_21_4_7_9_c(String & test) {
+  String str;
+  randomString(&str, maxString);
+  int tristate = test.compare(
+    random(0, test.size()),
+    random(0, test.size()),
+    str,
+    random(0, str.size()),
+    random(0, str.size()));
+  if (tristate > 0) tristate = 1;
+  else if (tristate < 0) tristate = 2;
+  Num2String(test, tristate);
+}
+
+template <class String> void clause11_21_4_7_9_d(String & test) {
+  String s;
+  randomString(&s, maxString);
+  int tristate = test.compare(s.c_str());
+  if (tristate > 0) tristate = 1;
+  else if (tristate < 0) tristate = 2;
+                Num2String(test, tristate);
+}
+
+template <class String> void clause11_21_4_7_9_e(String & test) {
+  String str;
+  randomString(&str, maxString);
+  int tristate = test.compare(
+    random(0, test.size()),
+    random(0, test.size()),
+    str.c_str(),
+    random(0, str.size()));
+  if (tristate > 0) tristate = 1;
+  else if (tristate < 0) tristate = 2;
+  Num2String(test, tristate);
+}
+
+template <class String> void clause11_21_4_8_1_a(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = s1 + s2;
+}
+
+template <class String> void clause11_21_4_8_1_b(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = move(s1) + s2;
+}
+
+template <class String> void clause11_21_4_8_1_c(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = s1 + move(s2);
+}
+
+template <class String> void clause11_21_4_8_1_d(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = move(s1) + move(s2);
+}
+
+template <class String> void clause11_21_4_8_1_e(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = s.c_str() + s1;
+}
+
+template <class String> void clause11_21_4_8_1_f(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = s.c_str() + move(s1);
+}
+
+template <class String> void clause11_21_4_8_1_g(String & test) {
+  String s;
+  randomString(&s, maxString);
+  test = typename String::value_type(random('a', 'z')) + s;
+}
+
+template <class String> void clause11_21_4_8_1_h(String & test) {
+  String s;
+  randomString(&s, maxString);
+  test = typename String::value_type(random('a', 'z')) + move(s);
+}
+
+template <class String> void clause11_21_4_8_1_i(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = s + s1.c_str();
+}
+
+template <class String> void clause11_21_4_8_1_j(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = move(s) + s1.c_str();
+}
+
+template <class String> void clause11_21_4_8_1_k(String & test) {
+  String s;
+  randomString(&s, maxString);
+  test = s + typename String::value_type(random('a', 'z'));
+}
+
+template <class String> void clause11_21_4_8_1_l(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = move(s) + s1.c_str();
+}
+
+// Numbering here is from C++11
+template <class String> void clause11_21_4_8_9_a(String & test) {
+  basic_stringstream<typename String::value_type> stst(test.c_str());
+  String str;
+  while (stst) {
+    stst >> str;
+    test += str + test;
+  }
+}
+
+TEST(FBString, testAllClauses) {
+  EXPECT_TRUE(1) << "Starting with seed: " << seed;
+  std::string r;
+  std::wstring wr;
+  folly::fbstring c;
+  folly::basic_fbstring<wchar_t> wc;
+  int count = 0;
+
+  auto l = [&](const char * const clause,
+               void(*f_string)(std::string&),
+               void(*f_fbstring)(folly::fbstring&),
+               void(*f_wfbstring)(folly::basic_fbstring<wchar_t>&)) {
+    do {
+      if (1) {} else EXPECT_TRUE(1) << "Testing clause " << clause;
+      randomString(&r);
+      c = r;
+      EXPECT_EQ(c, r);
+      wr = std::wstring(r.begin(), r.end());
+      wc = folly::basic_fbstring<wchar_t>(wr.c_str());
+      auto localSeed = seed + count;
+      rng = RandomT(localSeed);
+      f_string(r);
+      rng = RandomT(localSeed);
+      f_fbstring(c);
+      EXPECT_EQ(r, c)
+        << "Lengths: " << r.size() << " vs. " << c.size()
+        << "\nReference: '" << r << "'"
+        << "\nActual:    '" << c.data()[0] << "'";
+      rng = RandomT(localSeed);
+      f_wfbstring(wc);
+      int wret = wcslen(wc.c_str());
+      std::string mb(wret+1, '\0');
+      int ret = wcstombs(const_cast<char *>(mb.data()), wc.c_str(), mb.size());
+      while (!mb.empty() && mb.back() == '\0') {
+          mb.pop_back();
+      }
+      if (ret == wret) mb[wret] = '\0';
+      const char *mc = c.c_str();
+      std::string one(mb);
+      std::string two(mc);
+      EXPECT_EQ(one, two);
+    } while (++count % 100 != 0);
+  };
+
+#define TEST_CLAUSE(x) \
+  l(#x, \
+    clause11_##x<std::string>, \
+    clause11_##x<folly::fbstring>, \
+    clause11_##x<folly::basic_fbstring<wchar_t>>);
+
+  TEST_CLAUSE(21_4_2_a);
+  TEST_CLAUSE(21_4_2_b);
+  TEST_CLAUSE(21_4_2_c);
+  TEST_CLAUSE(21_4_2_d);
+  TEST_CLAUSE(21_4_2_e);
+  TEST_CLAUSE(21_4_2_f);
+  TEST_CLAUSE(21_4_2_g);
+  TEST_CLAUSE(21_4_2_h);
+  TEST_CLAUSE(21_4_2_i);
+  TEST_CLAUSE(21_4_2_j);
+  TEST_CLAUSE(21_4_2_k);
+  TEST_CLAUSE(21_4_2_l);
+  TEST_CLAUSE(21_4_2_lprime);
+  TEST_CLAUSE(21_4_2_m);
+  TEST_CLAUSE(21_4_2_n);
+  TEST_CLAUSE(21_4_3);
+  TEST_CLAUSE(21_4_4);
+  TEST_CLAUSE(21_4_5);
+  TEST_CLAUSE(21_4_6_1);
+  TEST_CLAUSE(21_4_6_2);
+  TEST_CLAUSE(21_4_6_3_a);
+  TEST_CLAUSE(21_4_6_3_b);
+  TEST_CLAUSE(21_4_6_3_c);
+  TEST_CLAUSE(21_4_6_3_d);
+  TEST_CLAUSE(21_4_6_3_e);
+  TEST_CLAUSE(21_4_6_3_f);
+  TEST_CLAUSE(21_4_6_3_g);
+  TEST_CLAUSE(21_4_6_3_h);
+  TEST_CLAUSE(21_4_6_3_i);
+  TEST_CLAUSE(21_4_6_3_j);
+  TEST_CLAUSE(21_4_6_3_k);
+  TEST_CLAUSE(21_4_6_4);
+  TEST_CLAUSE(21_4_6_5);
+  TEST_CLAUSE(21_4_6_6);
+  TEST_CLAUSE(21_4_6_7);
+  TEST_CLAUSE(21_4_6_8);
+  TEST_CLAUSE(21_4_7_1);
+
+  TEST_CLAUSE(21_4_7_2_a);
+  TEST_CLAUSE(21_4_7_2_a1);
+  TEST_CLAUSE(21_4_7_2_a2);
+  TEST_CLAUSE(21_4_7_2_b);
+  TEST_CLAUSE(21_4_7_2_b1);
+  TEST_CLAUSE(21_4_7_2_b2);
+  TEST_CLAUSE(21_4_7_2_c);
+  TEST_CLAUSE(21_4_7_2_c1);
+  TEST_CLAUSE(21_4_7_2_c2);
+  TEST_CLAUSE(21_4_7_2_d);
+  TEST_CLAUSE(21_4_7_3_a);
+  TEST_CLAUSE(21_4_7_3_b);
+  TEST_CLAUSE(21_4_7_3_c);
+  TEST_CLAUSE(21_4_7_3_d);
+  TEST_CLAUSE(21_4_7_4_a);
+  TEST_CLAUSE(21_4_7_4_b);
+  TEST_CLAUSE(21_4_7_4_c);
+  TEST_CLAUSE(21_4_7_4_d);
+  TEST_CLAUSE(21_4_7_5_a);
+  TEST_CLAUSE(21_4_7_5_b);
+  TEST_CLAUSE(21_4_7_5_c);
+  TEST_CLAUSE(21_4_7_5_d);
+  TEST_CLAUSE(21_4_7_6_a);
+  TEST_CLAUSE(21_4_7_6_b);
+  TEST_CLAUSE(21_4_7_6_c);
+  TEST_CLAUSE(21_4_7_6_d);
+  TEST_CLAUSE(21_4_7_7_a);
+  TEST_CLAUSE(21_4_7_7_b);
+  TEST_CLAUSE(21_4_7_7_c);
+  TEST_CLAUSE(21_4_7_7_d);
+  TEST_CLAUSE(21_4_7_8);
+  TEST_CLAUSE(21_4_7_9_a);
+  TEST_CLAUSE(21_4_7_9_b);
+  TEST_CLAUSE(21_4_7_9_c);
+  TEST_CLAUSE(21_4_7_9_d);
+  TEST_CLAUSE(21_4_7_9_e);
+  TEST_CLAUSE(21_4_8_1_a);
+  TEST_CLAUSE(21_4_8_1_b);
+  TEST_CLAUSE(21_4_8_1_c);
+  TEST_CLAUSE(21_4_8_1_d);
+  TEST_CLAUSE(21_4_8_1_e);
+  TEST_CLAUSE(21_4_8_1_f);
+  TEST_CLAUSE(21_4_8_1_g);
+  TEST_CLAUSE(21_4_8_1_h);
+  TEST_CLAUSE(21_4_8_1_i);
+  TEST_CLAUSE(21_4_8_1_j);
+  TEST_CLAUSE(21_4_8_1_k);
+  TEST_CLAUSE(21_4_8_1_l);
+  TEST_CLAUSE(21_4_8_9_a);
+}
+
+TEST(FBString, testGetline) {
+  fbstring s1 = "\
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras accumsan \n\
+elit ut urna consectetur in sagittis mi auctor. Nulla facilisi. In nec \n\
+dolor leo, vitae imperdiet neque. Donec ut erat mauris, a faucibus \n\
+elit. Integer consectetur gravida augue, sit amet mattis mauris auctor \n\
+sed. Morbi congue libero eu nunc sodales adipiscing. In lectus nunc, \n\
+vulputate a fringilla at, venenatis quis justo. Proin eu velit \n\
+nibh. Maecenas vitae tellus eros. Pellentesque habitant morbi \n\
+tristique senectus et netus et malesuada fames ac turpis \n\
+egestas. Vivamus faucibus feugiat consequat. Donec fermentum neque sit \n\
+amet ligula suscipit porta. Phasellus facilisis felis in purus luctus \n\
+quis posuere leo tempor. Nam nunc purus, luctus a pharetra ut, \n\
+placerat at dui. Donec imperdiet, diam quis convallis pulvinar, dui \n\
+est commodo lorem, ut tincidunt diam nibh et nibh. Maecenas nec velit \n\
+massa, ut accumsan magna. Donec imperdiet tempor nisi et \n\
+laoreet. Phasellus lectus quam, ultricies ut tincidunt in, dignissim \n\
+id eros. Mauris vulputate tortor nec neque pellentesque sagittis quis \n\
+sed nisl. In diam lacus, lobortis ut posuere nec, ornare id quam.";
+#ifdef ANDROID
+  char f[] = "/storage/fbstring_testing.XXXXXX";
+#else
+  char f[] = "/tmp/fbstring_testing.XXXXXX";
+#endif
+  int fd = mkstemp(f);
+  EXPECT_TRUE(fd > 0);
+  if (fd > 0) {
+    close(fd);  // Yeah
+    std::ofstream out(f);
+    if (!(out << s1)) {
+      EXPECT_TRUE(0) << "Couldn't write to temp file.";
+      return;
+    }
+  }
+  vector<fbstring> v;
+  boost::split(v, s1, boost::is_any_of("\n"));
+  {
+    ifstream input(f);
+    fbstring line;
+    FOR_EACH (i, v) {
+      EXPECT_TRUE(!getline(input, line).fail());
+      EXPECT_EQ(line, *i);
+    }
+  }
+  unlink(f);
+}
+
+TEST(FBString, testMoveCtor) {
+  // Move constructor. Make sure we allocate a large string, so the
+  // small string optimization doesn't kick in.
+  auto size = random(100, 2000);
+  fbstring s(size, 'a');
+  fbstring test = std::move(s);
+  EXPECT_TRUE(s.empty());
+  EXPECT_EQ(size, test.size());
+}
+
+TEST(FBString, testMoveAssign) {
+  // Move constructor. Make sure we allocate a large string, so the
+  // small string optimization doesn't kick in.
+  auto size = random(100, 2000);
+  fbstring s(size, 'a');
+  fbstring test;
+  test = std::move(s);
+  EXPECT_TRUE(s.empty());
+  EXPECT_EQ(size, test.size());
+}
+
+TEST(FBString, testMoveOperatorPlusLhs) {
+  // Make sure we allocate a large string, so the
+  // small string optimization doesn't kick in.
+  auto size1 = random(100, 2000);
+  auto size2 = random(100, 2000);
+  fbstring s1(size1, 'a');
+  fbstring s2(size2, 'b');
+  fbstring test;
+  test = std::move(s1) + s2;
+  EXPECT_TRUE(s1.empty());
+  EXPECT_EQ(size1 + size2, test.size());
+}
+
+TEST(FBString, testMoveOperatorPlusRhs) {
+  // Make sure we allocate a large string, so the
+  // small string optimization doesn't kick in.
+  auto size1 = random(100, 2000);
+  auto size2 = random(100, 2000);
+  fbstring s1(size1, 'a');
+  fbstring s2(size2, 'b');
+  fbstring test;
+  test = s1 + std::move(s2);
+  EXPECT_EQ(size1 + size2, test.size());
+}
+
+// The GNU C++ standard library throws an std::logic_error when an std::string
+// is constructed with a null pointer. Verify that we mirror this behavior.
+//
+// N.B. We behave this way even if the C++ library being used is something
+//      other than libstdc++. Someday if we deem it important to present
+//      identical undefined behavior for other platforms, we can re-visit this.
+TEST(FBString, testConstructionFromLiteralZero) {
+  EXPECT_THROW(fbstring s(0), std::logic_error);
+}
+
+TEST(FBString, testFixedBugs) {
+  { // D479397
+    fbstring str(1337, 'f');
+    fbstring cp = str;
+    cp.clear();
+    cp.c_str();
+    EXPECT_EQ(str.front(), 'f');
+  }
+  { // D481173
+    fbstring str(1337, 'f');
+    for (int i = 0; i < 2; ++i) {
+      fbstring cp = str;
+      cp[1] = 'b';
+      EXPECT_EQ(cp.c_str()[cp.size()], '\0');
+      cp.push_back('?');
+    }
+  }
+  { // D580267
+    {
+      fbstring str(1337, 'f');
+      fbstring cp = str;
+      cp.push_back('f');
+    }
+    {
+      fbstring str(1337, 'f');
+      fbstring cp = str;
+      cp += "bb";
+    }
+  }
+  { // D661622
+    folly::basic_fbstring<wchar_t> s;
+    EXPECT_EQ(0, s.size());
+  }
+  { // D785057
+    fbstring str(1337, 'f');
+    std::swap(str, str);
+    EXPECT_EQ(1337, str.size());
+  }
+  { // D1012196, --allocator=malloc
+    fbstring str(128, 'f');
+    str.clear();  // Empty medium string.
+    fbstring copy(str);  // Medium string of 0 capacity.
+    copy.push_back('b');
+    EXPECT_GE(copy.capacity(), 1);
+  }
+}
+
+TEST(FBString, findWithNpos) {
+  fbstring fbstr("localhost:80");
+  EXPECT_EQ(fbstring::npos, fbstr.find(":", fbstring::npos));
+}
+
+TEST(FBString, testHash) {
+  fbstring a;
+  fbstring b;
+  a.push_back(0);
+  a.push_back(1);
+  b.push_back(0);
+  b.push_back(2);
+  std::hash<fbstring> hashfunc;
+  EXPECT_NE(hashfunc(a), hashfunc(b));
+}
+
+TEST(FBString, testFrontBack) {
+  fbstring str("hello");
+  EXPECT_EQ(str.front(), 'h');
+  EXPECT_EQ(str.back(), 'o');
+  str.front() = 'H';
+  EXPECT_EQ(str.front(), 'H');
+  str.back() = 'O';
+  EXPECT_EQ(str.back(), 'O');
+  EXPECT_EQ(str, "HellO");
+}
+
+TEST(FBString, noexcept) {
+  EXPECT_TRUE(noexcept(fbstring()));
+  // std::move is not marked noexcept in gcc 4.6, sigh
+#if __GNUC_PREREQ(4, 7)
+  fbstring x;
+  EXPECT_FALSE(noexcept(fbstring(x)));
+  EXPECT_TRUE(noexcept(fbstring(std::move(x))));
+  fbstring y;
+  EXPECT_FALSE(noexcept(y = x));
+  EXPECT_TRUE(noexcept(y = std::move(x)));
+#endif
+}
+
+TEST(FBString, iomanip) {
+  stringstream ss;
+  fbstring fbstr("Hello");
+
+  ss << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), " Hello");
+  ss.str("");
+
+  ss << left << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), "Hello ");
+  ss.str("");
+
+  ss << right << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), " Hello");
+  ss.str("");
+
+  ss << setw(4) << fbstr;
+  EXPECT_EQ(ss.str(), "Hello");
+  ss.str("");
+
+  ss << setfill('^') << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), "^Hello");
+  ss.str("");
+}
+
+TEST(FBString, rvalueIterators) {
+  // you cannot take &* of a move-iterator, so use that for testing
+  fbstring s = "base";
+  fbstring r = "hello";
+  r.replace(r.begin(), r.end(),
+      make_move_iterator(s.begin()), make_move_iterator(s.end()));
+  EXPECT_EQ("base", r);
+
+  // The following test is probably not required by the standard.
+  // i.e. this could be in the realm of undefined behavior.
+  fbstring b = "123abcXYZ";
+  auto ait = b.begin() + 3;
+  auto Xit = b.begin() + 6;
+  b.replace(ait, b.end(), b.begin(), Xit);
+  EXPECT_EQ("123123abc", b); // if things go wrong, you'd get "123123123"
+}
+
+TEST(FBString, moveTerminator) {
+  // The source of a move must remain in a valid state
+  fbstring s(100, 'x'); // too big to be in-situ
+  fbstring k;
+  k = std::move(s);
+
+  EXPECT_EQ(0, s.size());
+  EXPECT_EQ('\0', *s.c_str());
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/FBStringTestBenchmarks.cpp.h b/faux-folly/folly/test/FBStringTestBenchmarks.cpp.h
new file mode 100644
index 0000000..e44fd00
--- /dev/null
+++ b/faux-folly/folly/test/FBStringTestBenchmarks.cpp.h
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * This file is supposed to be included from within
+ * FBStringTest. Do not use otherwise.
+ *
+ * override-include-guard
+ */
+
+void BENCHFUN(initRNG)(size_t iters, size_t) {
+  srand(seed);
+}
+BENCHMARK_PARAM(BENCHFUN(initRNG), 0);
+
+void BENCHFUN(defaultCtor)(size_t iters, size_t) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s[4096];
+    doNotOptimizeAway(&s);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(defaultCtor), 0);
+
+void BENCHFUN(copyCtor)(size_t iters, size_t arg) {
+  STRING s;
+  BENCHMARK_SUSPEND {
+    randomString(&s, arg);
+  }
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s1 = s;
+    doNotOptimizeAway(&s1);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(copyCtor), 32768);
+
+void BENCHFUN(ctorFromArray)(size_t iters, size_t arg) {
+  STRING s;
+  BENCHMARK_SUSPEND {
+    randomString(&s, arg);
+    if (s.empty()) {
+      s = "This is rare.";
+    }
+  }
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s1(s.data(), s.size());
+    doNotOptimizeAway(&s1);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(ctorFromArray), 32768);
+
+void BENCHFUN(ctorFromTwoPointers)(size_t iters, size_t arg) {
+  static STRING s;
+  BENCHMARK_SUSPEND {
+    if (s.size() < arg) s.resize(arg);
+  }
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s1(s.begin(), s.end());
+    doNotOptimizeAway(&s1);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(ctorFromTwoPointers), 0);
+BENCHMARK_PARAM(BENCHFUN(ctorFromTwoPointers), 7);
+BENCHMARK_PARAM(BENCHFUN(ctorFromTwoPointers), 15);
+BENCHMARK_PARAM(BENCHFUN(ctorFromTwoPointers), 23);
+BENCHMARK_PARAM(BENCHFUN(ctorFromTwoPointers), 24);
+
+void BENCHFUN(ctorFromChar)(size_t iters, size_t arg) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s1('a', arg);
+    doNotOptimizeAway(&s1);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(ctorFromChar), 1048576);
+
+void BENCHFUN(assignmentOp)(size_t iters, size_t arg) {
+  STRING s;
+  BENCHMARK_SUSPEND {
+    randomString(&s, arg);
+  }
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s1;
+    BENCHMARK_SUSPEND {
+      randomString(&s1, arg);
+      doNotOptimizeAway(&s1);
+    }
+    s1 = s;
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(assignmentOp), 256);
+
+void BENCHFUN(assignmentFill)(size_t iters, size_t) {
+  STRING s;
+  FOR_EACH_RANGE (i, 0, iters) {
+    s = static_cast<char>(i);
+    doNotOptimizeAway(&s);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(assignmentFill), 0);
+
+void BENCHFUN(resize)(size_t iters, size_t arg) {
+  STRING s;
+  FOR_EACH_RANGE (i, 0, iters) {
+    s.resize(random(0, arg));
+    doNotOptimizeAway(&s);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(resize), 524288);
+
+void BENCHFUN(findSuccessful)(size_t iters, size_t arg) {
+  size_t pos, len;
+  STRING s;
+
+  BENCHMARK_SUSPEND {
+
+    // Text courtesy (ahem) of
+    // http://www.psychologytoday.com/blog/career-transitions/200906/
+    // the-dreaded-writing-sample
+    s = "\
+Even if you've mastered the art of the cover letter and the resume, \
+another part of the job search process can trip up an otherwise \
+qualified candidate: the writing sample.\n\
+\n\
+Strong writing and communication skills are highly sought after by \
+most employers. Whether crafting short emails or lengthy annual \
+reports, many workers use their writing skills every day. And for an \
+employer seeking proof behind that ubiquitous candidate \
+phrase,\"excellent communication skills\", a required writing sample \
+is invaluable.\n\
+\n\
+Writing samples need the same care and attention given to cover \
+letters and resumes. Candidates with otherwise impeccable credentials \
+are routinely eliminated by a poorly chosen writing sample. Notice I \
+said \"poorly chosen\" not \"poorly written.\" Because that's the rub: \
+a writing sample not only reveals the individual's writing skills, it \
+also offers a peek into what they consider important or relevant for \
+the position. If you miss that mark with your writing sample, don't \
+expect to get a call for an interview.";
+
+    pos = random(0, s.size());
+    len = random(0, s.size() - pos);
+  }
+  FOR_EACH_RANGE (i, 0, iters) {
+    doNotOptimizeAway(s.find(s.data(), pos, len));
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(findSuccessful), 524288);
+
+void BENCHFUN(findUnsuccessful)(size_t iters, size_t arg) {
+  STRING s, s1;
+
+  BENCHMARK_SUSPEND {
+    s = "\
+Even if you've mastered the art of the cover letter and the resume, \
+another part of the job search process can trip up an otherwise \
+qualified candidate: the writing sample.\n\
+\n\
+Strong writing and communication skills are highly sought after by \
+most employers. Whether crafting short emails or lengthy annual \
+reports, many workers use their writing skills every day. And for an \
+employer seeking proof behind that ubiquitous candidate \
+phrase,\"excellent communication skills\", a required writing sample \
+is invaluable.\n\
+\n\
+Writing samples need the same care and attention given to cover \
+letters and resumes. Candidates with otherwise impeccable credentials \
+are routinely eliminated by a poorly chosen writing sample. Notice I \
+said \"poorly chosen\" not \"poorly written.\" Because that's the rub: \
+a writing sample not only reveals the individual's writing skills, it \
+also offers a peek into what they consider important or relevant for \
+the position. If you miss that mark with your writing sample, don't \
+expect to get a call for an interview.";
+
+    s1 = "So how do you tackle that writing sample request?";
+  }
+
+  FOR_EACH_RANGE (i, 0, iters) {
+    doNotOptimizeAway(s.find(s1));
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(findUnsuccessful), 524288);
+
+void BENCHFUN(equality)(size_t iters, size_t arg) {
+  std::vector<STRING> haystack(arg);
+
+  BENCHMARK_SUSPEND {
+    for (auto& hay : haystack) {
+      randomBinaryString(&hay, 1024);
+    }
+  }
+
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING needle;
+    randomBinaryString(&needle, 1024);
+    doNotOptimizeAway(std::find(haystack.begin(), haystack.end(), needle));
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(equality), 65536);
+
+void BENCHFUN(replace)(size_t iters, size_t arg) {
+  STRING s;
+  BENCHMARK_SUSPEND {
+    randomString(&s, arg);
+  }
+  FOR_EACH_RANGE (i, 0, iters) {
+    BenchmarkSuspender susp;
+    doNotOptimizeAway(&s);
+    auto const pos = random(0, s.size());
+    auto toRemove = random(0, s.size() - pos);
+    auto toInsert = random(0, arg);
+    STRING s1;
+    randomString(&s1, toInsert);
+    susp.dismiss();
+
+   s.replace(pos, toRemove, s1);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(replace), 256);
+
+void BENCHFUN(push_back)(size_t iters, size_t arg) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s;
+    FOR_EACH_RANGE (j, 0, arg) {
+      s += ' ';
+    }
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(push_back), 1);
+BENCHMARK_PARAM(BENCHFUN(push_back), 23);
+BENCHMARK_PARAM(BENCHFUN(push_back), 127);
+BENCHMARK_PARAM(BENCHFUN(push_back), 1024);
+
+void BENCHFUN(short_append)(size_t iters, size_t arg) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    STRING s;
+    FOR_EACH_RANGE (j, 0, arg) {
+      s += "012";
+    }
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(short_append), 23);
+BENCHMARK_PARAM(BENCHFUN(short_append), 1024);
diff --git a/faux-folly/folly/test/FBVectorBenchmark.cpp b/faux-folly/folly/test/FBVectorBenchmark.cpp
new file mode 100644
index 0000000..6548c3f
--- /dev/null
+++ b/faux-folly/folly/test/FBVectorBenchmark.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2015 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.
+ */
+
+//
+// Author: andrei.alexandrescu@fb.com
+
+#include <folly/Traits.h>
+#include <folly/Random.h>
+#include <folly/FBString.h>
+#include <folly/FBVector.h>
+#include <folly/Benchmark.h>
+
+#include <gflags/gflags.h>
+
+#include <gtest/gtest.h>
+#include <list>
+#include <memory>
+#include <boost/random.hpp>
+
+using namespace std;
+using namespace folly;
+
+auto static const seed = randomNumberSeed();
+typedef boost::mt19937 RandomT;
+static RandomT rng(seed);
+static const size_t maxString = 100;
+static const bool avoidAliasing = true;
+
+template <class Integral1, class Integral2>
+Integral2 random(Integral1 low, Integral2 up) {
+  boost::uniform_int<> range(low, up);
+  return range(rng);
+}
+
+template <class String>
+void randomString(String* toFill, unsigned int maxSize = 1000) {
+  assert(toFill);
+  toFill->resize(random(0, maxSize));
+  FOR_EACH (i, *toFill) {
+    *i = random('a', 'z');
+  }
+}
+
+template <class String, class Integral>
+void Num2String(String& str, Integral n) {
+  str.resize(10, '\0');
+  sprintf(&str[0], "%ul", 10);
+  str.resize(strlen(str.c_str()));
+}
+
+std::list<char> RandomList(unsigned int maxSize) {
+  std::list<char> lst(random(0u, maxSize));
+  std::list<char>::iterator i = lst.begin();
+  for (; i != lst.end(); ++i) {
+    *i = random('a', 'z');
+  }
+  return lst;
+}
+
+template<class T> T randomObject();
+
+template<> int randomObject<int>() {
+  return random(0, 1024);
+}
+
+template<> folly::fbstring randomObject<folly::fbstring>() {
+  folly::fbstring result;
+  randomString(&result);
+  return result;
+}
+
+#define CONCAT(A, B) CONCAT_HELPER(A, B)
+#define CONCAT_HELPER(A, B) A##B
+#define BENCHFUN(F) CONCAT(CONCAT(BM_, F), CONCAT(_, VECTOR))
+#define TESTFUN(F) TEST(fbvector, CONCAT(F, VECTOR))
+
+typedef vector<int> IntVector;
+typedef fbvector<int> IntFBVector;
+typedef vector<folly::fbstring> FBStringVector;
+typedef fbvector<folly::fbstring> FBStringFBVector;
+
+#define VECTOR IntVector
+#include <folly/test/FBVectorTestBenchmarks.cpp.h> // nolint
+#undef VECTOR
+#define VECTOR IntFBVector
+#include <folly/test/FBVectorTestBenchmarks.cpp.h> // nolint
+#undef VECTOR
+#define VECTOR FBStringVector
+#include <folly/test/FBVectorTestBenchmarks.cpp.h> // nolint
+#undef VECTOR
+#define VECTOR FBStringFBVector
+#include <folly/test/FBVectorTestBenchmarks.cpp.h> // nolint
+#undef VECTOR
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
diff --git a/faux-folly/folly/test/FBVectorTest.cpp b/faux-folly/folly/test/FBVectorTest.cpp
new file mode 100644
index 0000000..330dc4b
--- /dev/null
+++ b/faux-folly/folly/test/FBVectorTest.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2015 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.
+ */
+
+//
+// Author: andrei.alexandrescu@fb.com
+
+#include <folly/Traits.h>
+#include <folly/Random.h>
+#include <folly/FBString.h>
+#include <folly/FBVector.h>
+
+#include <gflags/gflags.h>
+
+#include <gtest/gtest.h>
+#include <list>
+#include <map>
+#include <memory>
+#include <boost/random.hpp>
+
+using namespace std;
+using namespace folly;
+
+auto static const seed = randomNumberSeed();
+typedef boost::mt19937 RandomT;
+static RandomT rng(seed);
+static const size_t maxString = 100;
+static const bool avoidAliasing = true;
+
+template <class Integral1, class Integral2>
+Integral2 random(Integral1 low, Integral2 up) {
+  boost::uniform_int<> range(low, up);
+  return range(rng);
+}
+
+template <class String>
+void randomString(String* toFill, unsigned int maxSize = 1000) {
+  assert(toFill);
+  toFill->resize(random(0, maxSize));
+  FOR_EACH (i, *toFill) {
+    *i = random('a', 'z');
+  }
+}
+
+template <class String, class Integral>
+void Num2String(String& str, Integral n) {
+  str.resize(10, '\0');
+  sprintf(&str[0], "%ul", 10);
+  str.resize(strlen(str.c_str()));
+}
+
+std::list<char> RandomList(unsigned int maxSize) {
+  std::list<char> lst(random(0u, maxSize));
+  std::list<char>::iterator i = lst.begin();
+  for (; i != lst.end(); ++i) {
+    *i = random('a', 'z');
+  }
+  return lst;
+}
+
+template<class T> T randomObject();
+
+template<> int randomObject<int>() {
+  return random(0, 1024);
+}
+
+template<> folly::fbstring randomObject<folly::fbstring>() {
+  folly::fbstring result;
+  randomString(&result);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests begin here
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(fbvector, clause_23_3_6_1_3_ambiguity) {
+  fbvector<int> v(10, 20);
+  EXPECT_EQ(v.size(), 10);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, 20);
+  }
+}
+
+TEST(fbvector, clause_23_3_6_1_11_ambiguity) {
+  fbvector<int> v;
+  v.assign(10, 20);
+  EXPECT_EQ(v.size(), 10);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, 20);
+  }
+}
+
+TEST(fbvector, clause_23_3_6_2_6) {
+  fbvector<int> v;
+  auto const n = random(0U, 10000U);
+  v.reserve(n);
+  auto const n1 = random(0U, 10000U);
+  auto const obj = randomObject<int>();
+  v.assign(n1, obj);
+  v.shrink_to_fit();
+  // Nothing to verify except that the call made it through
+}
+
+TEST(fbvector, clause_23_3_6_4_ambiguity) {
+  fbvector<int> v;
+  fbvector<int>::const_iterator i = v.end();
+  v.insert(i, 10, 20);
+  EXPECT_EQ(v.size(), 10);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, 20);
+  }
+}
+
+TEST(fbvector, composition) {
+  fbvector< fbvector<double> > matrix(100, fbvector<double>(100));
+}
+
+TEST(fbvector, works_with_std_string) {
+  fbvector<std::string> v(10, "hello");
+  EXPECT_EQ(v.size(), 10);
+  v.push_back("world");
+}
+
+namespace {
+class UserDefinedType { int whatevs_; };
+}
+
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE(UserDefinedType);
+
+TEST(fbvector, works_with_user_defined_type) {
+  fbvector<UserDefinedType> v(10);
+  EXPECT_EQ(v.size(), 10);
+  v.push_back(UserDefinedType());
+}
+
+TEST(fbvector, move_construction) {
+  fbvector<int> v1(100, 100);
+  fbvector<int> v2;
+  EXPECT_EQ(v1.size(), 100);
+  EXPECT_EQ(v1.front(), 100);
+  EXPECT_EQ(v2.size(), 0);
+  v2 = std::move(v1);
+  EXPECT_EQ(v1.size(), 0);
+  EXPECT_EQ(v2.size(), 100);
+  EXPECT_EQ(v2.front(), 100);
+
+  v1.assign(100, 100);
+  auto other = std::move(v1);
+  EXPECT_EQ(v1.size(), 0);
+  EXPECT_EQ(other.size(), 100);
+  EXPECT_EQ(other.front(), 100);
+}
+
+TEST(fbvector, emplace) {
+  fbvector<std::string> s(12, "asd");
+  EXPECT_EQ(s.size(), 12);
+  EXPECT_EQ(s.front(), "asd");
+  s.emplace_back("funk");
+  EXPECT_EQ(s.back(), "funk");
+}
+
+TEST(fbvector, initializer_lists) {
+  fbvector<int> vec = { 1, 2, 3 };
+  EXPECT_EQ(vec.size(), 3);
+  EXPECT_EQ(vec[0], 1);
+  EXPECT_EQ(vec[1], 2);
+  EXPECT_EQ(vec[2], 3);
+
+  vec = { 0, 0, 12, 16 };
+  EXPECT_EQ(vec.size(), 4);
+  EXPECT_EQ(vec[0], 0);
+  EXPECT_EQ(vec[1], 0);
+  EXPECT_EQ(vec[2], 12);
+  EXPECT_EQ(vec[3], 16);
+
+  vec.insert(vec.begin() + 1, { 23, 23 });
+  EXPECT_EQ(vec.size(), 6);
+  EXPECT_EQ(vec[0], 0);
+  EXPECT_EQ(vec[1], 23);
+  EXPECT_EQ(vec[2], 23);
+  EXPECT_EQ(vec[3], 0);
+  EXPECT_EQ(vec[4], 12);
+  EXPECT_EQ(vec[5], 16);
+}
+
+TEST(fbvector, unique_ptr) {
+  fbvector<std::unique_ptr<int> > v(12);
+  std::unique_ptr<int> p(new int(12));
+  v.push_back(std::move(p));
+  EXPECT_EQ(*v.back(), 12);
+
+  v[0] = std::move(p);
+  EXPECT_FALSE(v[0].get());
+  v[0].reset(new int(32));
+  std::unique_ptr<int> somePtr;
+  v.insert(v.begin(), std::move(somePtr));
+  EXPECT_EQ(*v[1], 32);
+}
+
+TEST(FBVector, task858056) {
+  fbvector<fbstring> cycle;
+  cycle.push_back("foo");
+  cycle.push_back("bar");
+  cycle.push_back("baz");
+  fbstring message("Cycle detected: ");
+  FOR_EACH_R (node_name, cycle) {
+    message += "[";
+    message += *node_name;
+    message += "] ";
+  }
+  EXPECT_EQ("Cycle detected: [baz] [bar] [foo] ", message);
+}
+
+TEST(FBVector, move_iterator) {
+  fbvector<int> base = { 0, 1, 2 };
+
+  auto cp1 = base;
+  fbvector<int> fbvi1(std::make_move_iterator(cp1.begin()),
+                      std::make_move_iterator(cp1.end()));
+  EXPECT_EQ(fbvi1, base);
+
+  auto cp2 = base;
+  fbvector<int> fbvi2;
+  fbvi2.assign(std::make_move_iterator(cp2.begin()),
+               std::make_move_iterator(cp2.end()));
+  EXPECT_EQ(fbvi2, base);
+
+  auto cp3 = base;
+  fbvector<int> fbvi3;
+  fbvi3.insert(fbvi3.end(),
+               std::make_move_iterator(cp3.begin()),
+               std::make_move_iterator(cp3.end()));
+  EXPECT_EQ(fbvi3, base);
+}
+
+TEST(FBVector, reserve_consistency) {
+  struct S { int64_t a, b, c, d; };
+
+  fbvector<S> fb1;
+  for (size_t i = 0; i < 1000; ++i) {
+    fb1.reserve(1);
+    EXPECT_EQ(fb1.size(), 0);
+    fb1.shrink_to_fit();
+  }
+}
+
+TEST(FBVector, vector_of_maps) {
+  fbvector<std::map<std::string, std::string>> v;
+
+  v.push_back(std::map<std::string, std::string>());
+  v.push_back(std::map<std::string, std::string>());
+
+  EXPECT_EQ(2, v.size());
+
+  v[1]["hello"] = "world";
+  EXPECT_EQ(0, v[0].size());
+  EXPECT_EQ(1, v[1].size());
+
+  v[0]["foo"] = "bar";
+  EXPECT_EQ(1, v[0].size());
+  EXPECT_EQ(1, v[1].size());
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/FBVectorTestBenchmarks.cpp.h b/faux-folly/folly/test/FBVectorTestBenchmarks.cpp.h
new file mode 100644
index 0000000..e5d8b70
--- /dev/null
+++ b/faux-folly/folly/test/FBVectorTestBenchmarks.cpp.h
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * This file is supposed to be included from within
+ * FBVectorTest. Do not use otherwise.
+ */
+
+TESTFUN(clause_23_3_6_1_1) {
+  VECTOR v;
+  EXPECT_TRUE(v.empty());
+  VECTOR::allocator_type a;
+  VECTOR v1(a);
+  EXPECT_TRUE(v1.empty());
+}
+
+TESTFUN(clause_23_3_6_1_3) {
+  auto const n = random(0U, 10000U);
+  VECTOR v(n);
+  EXPECT_EQ(v.size(), n);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, VECTOR::value_type());
+  }
+}
+
+TESTFUN(clause_23_3_6_1_9) {
+  // Insert with iterators
+  list<VECTOR::value_type> lst;
+  auto const n = random(0U, 10000U);
+  FOR_EACH_RANGE (i, 0, n) {
+    lst.push_back(randomObject<VECTOR::value_type>());
+  }
+  VECTOR v(lst.begin(), lst.end());
+  EXPECT_EQ(v.size(), lst.size());
+  size_t j = 0;
+  FOR_EACH (i, lst) {
+    EXPECT_EQ(v[j], *i);
+    j++;
+  }
+}
+
+TESTFUN(clause_23_3_6_1_11) {
+  // assign with iterators
+  list<VECTOR::value_type> lst;
+  auto const n = random(0U, 10000U);
+  FOR_EACH_RANGE (i, 0, n) {
+    lst.push_back(randomObject<VECTOR::value_type>());
+  }
+  VECTOR v;
+  v.assign(lst.begin(), lst.end());
+  EXPECT_EQ(v.size(), lst.size());
+  size_t j = 0;
+  FOR_EACH (i, lst) {
+    EXPECT_EQ(v[j], *i);
+    j++;
+  }
+
+  // aliased assign
+  v.assign(v.begin(), v.begin() + v.size() / 2);
+  EXPECT_EQ(v.size(), lst.size() / 2);
+  j = 0;
+  FOR_EACH (i, lst) {
+    if (j == v.size()) break;
+    EXPECT_EQ(v[j], *i);
+    j++;
+  }
+}
+
+TESTFUN(clause_23_3_6_1_12) {
+  VECTOR v;
+  auto const n = random(0U, 10000U);
+  auto const obj = randomObject<VECTOR::value_type>();
+  v.assign(n, obj);
+  EXPECT_EQ(v.size(), n);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, obj);
+  }
+}
+
+TESTFUN(clause_23_3_6_2_1) {
+  VECTOR v;
+  auto const n = random(0U, 10000U);
+  v.reserve(n);
+  EXPECT_GE(v.capacity(), n);
+}
+
+TESTFUN(clause_23_3_6_2_7) {
+  auto const n1 = random(0U, 10000U);
+  auto const n2 = random(0U, 10000U);
+  auto const obj1 = randomObject<VECTOR::value_type>();
+  auto const obj2 = randomObject<VECTOR::value_type>();
+  VECTOR v1(n1, obj1), v2(n2, obj2);
+  v1.swap(v2);
+  EXPECT_EQ(v1.size(), n2);
+  EXPECT_EQ(v2.size(), n1);
+  FOR_EACH (i, v1) {
+    EXPECT_EQ(*i, obj2);
+  }
+  FOR_EACH (i, v2) {
+    EXPECT_EQ(*i, obj1);
+  }
+}
+
+TESTFUN(clause_23_3_6_2_9) {
+  VECTOR v;
+  auto const n1 = random(0U, 10000U);
+  v.resize(n1);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, VECTOR::value_type());
+  }
+  auto const n2 = random(0U, 10000U);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, VECTOR::value_type());
+  }
+}
+
+TESTFUN(clause_23_3_6_2_11) {
+  VECTOR v;
+  auto const n1 = random(0U, 10000U);
+  auto const obj1 = randomObject<VECTOR::value_type>();
+  v.resize(n1, obj1);
+  FOR_EACH (i, v) {
+    EXPECT_EQ(*i, obj1);
+  }
+  auto const n2 = random(0U, 10000U);
+  auto const obj2 = randomObject<VECTOR::value_type>();
+  v.resize(n2, obj2);
+  if (n1 < n2) {
+    FOR_EACH_RANGE (i, n1, n2) {
+      EXPECT_EQ(v[i], obj2);
+    }
+  }
+}
+
+TESTFUN(clause_absent_element_access) {
+  VECTOR v;
+  auto const n1 = random(1U, 10000U);
+  auto const obj1 = randomObject<VECTOR::value_type>();
+  v.resize(n1, obj1);
+  auto const n = random(0U, v.size() - 1);
+  EXPECT_EQ(v[n], v.at(n));
+  auto const obj2 = randomObject<VECTOR::value_type>();
+  v[n] = obj2;
+  EXPECT_EQ(v[n], v.at(n));
+  EXPECT_EQ(v[n], obj2);
+  auto const obj3 = randomObject<VECTOR::value_type>();
+  v.at(n) = obj3;
+  EXPECT_EQ(v[n], v.at(n));
+  EXPECT_EQ(v[n], obj3);
+}
+
+TESTFUN(clause_23_3_6_3_1) {
+  VECTOR v;
+  auto const n1 = random(1U, 10000U);
+  auto const obj1 = randomObject<VECTOR::value_type>();
+  v.resize(n1, obj1);
+  EXPECT_EQ(v.data(), &v.front());
+}
+
+TESTFUN(clause_23_3_6_4_1_a) {
+  VECTOR v, w;
+  auto const n1 = random(1U, 10000U);
+  FOR_EACH_RANGE (i, 0, n1) {
+    auto const obj1 = randomObject<VECTOR::value_type>();
+    v.push_back(obj1);
+    w.push_back(obj1);
+  }
+  auto const n2 = random(0U, n1 - 1);
+  auto pos = v.begin() + n2;
+  auto const obj2 = randomObject<VECTOR::value_type>();
+
+  auto r = v.insert(pos, obj2);
+
+  EXPECT_EQ(v.size(), w.size() + 1);
+  EXPECT_EQ(r - v.begin(), n2);
+  EXPECT_EQ(*r, obj2);
+  FOR_EACH_RANGE (i, 0, r - v.begin()) {
+    EXPECT_EQ(v[i], w[i]);
+  }
+  FOR_EACH_RANGE (i, r - v.begin() + 1, v.size()) {
+    EXPECT_EQ(v[i], w[i - 1]);
+  }
+}
+
+TESTFUN(clause_23_3_6_4_1_c) {
+  // This test only works for fbvector
+  fbvector<VECTOR::value_type> v, w;
+  auto const n1 = random(1U, 10000U);
+  FOR_EACH_RANGE (i, 0, n1) {
+    auto const obj1 = randomObject<VECTOR::value_type>();
+    v.push_back(obj1);
+    w.push_back(obj1);
+  }
+  auto const n2 = random(0U, n1-1);
+  auto pos = v.begin() + n2;
+  auto const obj2 = randomObject<VECTOR::value_type>();
+  auto const n3 = random(0U, 10000U);
+
+  auto r = v.insert(pos, n3, obj2);
+
+  EXPECT_EQ(v.size(), w.size() + n3);
+  EXPECT_EQ(r - v.begin(), n2);
+  FOR_EACH_RANGE (i, 0, r - v.begin()) {
+    EXPECT_EQ(v[i], w[i]);
+  }
+  FOR_EACH_RANGE (i, r - v.begin(), r - v.begin() + n3) {
+    EXPECT_EQ(v[i], obj2);
+  }
+  FOR_EACH_RANGE (i, r - v.begin() + n3, v.size()) {
+    EXPECT_EQ(v[i], w[i - n3]);
+  }
+}
+
+TESTFUN(clause_23_3_6_4_1_d) {
+  VECTOR v, w;
+  auto const n1 = random(0U, 10000U);
+  FOR_EACH_RANGE (i, 0, n1) {
+    auto const obj1 = randomObject<VECTOR::value_type>();
+    v.push_back(obj1);
+    w.push_back(obj1);
+  }
+  EXPECT_EQ(v.size(), n1);
+
+  auto const obj2 = randomObject<VECTOR::value_type>();
+  v.push_back(obj2);
+  EXPECT_EQ(v.back(), obj2);
+  EXPECT_EQ(v.size(), w.size() + 1);
+
+  FOR_EACH_RANGE (i, 0, w.size()) {
+    EXPECT_EQ(v[i], w[i]);
+  }
+}
+
+TESTFUN(clause_23_3_6_4_3) {
+  VECTOR v, w;
+  auto const n1 = random(1U, 10000U);
+  FOR_EACH_RANGE (i, 0, n1) {
+    auto const obj1 = randomObject<VECTOR::value_type>();
+    v.push_back(obj1);
+    w.push_back(obj1);
+  }
+  EXPECT_EQ(v.size(), n1);
+
+  auto const n2 = random(0U, n1 - 1);
+  auto it = v.erase(v.begin() + n2);
+  EXPECT_EQ(v.size() + 1, w.size());
+
+  FOR_EACH_RANGE (i, 0, it - v.begin()) {
+    EXPECT_EQ(v[i], w[i]);
+  }
+
+  FOR_EACH_RANGE (i, it - v.begin(), v.size()) {
+    EXPECT_EQ(v[i], w[i + 1]);
+  }
+}
+
+TESTFUN(clause_23_3_6_4_4) {
+  VECTOR v, w;
+  auto const n1 = random(1U, 10000U);
+  FOR_EACH_RANGE (i, 0, n1) {
+    auto const obj1 = randomObject<VECTOR::value_type>();
+    v.push_back(obj1);
+    w.push_back(obj1);
+  }
+  EXPECT_EQ(v.size(), n1);
+
+  auto const n2 = random(0U, n1 - 1);
+  auto const n3 = random(n2, n1 - 1);
+  auto it = v.erase(v.begin() + n2, v.begin() + n3);
+  EXPECT_EQ(v.size() + (n3 - n2), w.size());
+
+  FOR_EACH_RANGE (i, 0, it - v.begin()) {
+    EXPECT_EQ(v[i], w[i]);
+  }
+
+  FOR_EACH_RANGE (i, it - v.begin(), v.size()) {
+    EXPECT_EQ(v[i], w[i + (n3 - n2)]);
+  }
+}
+
+TESTFUN(clause_23_3_6_4_clear) {
+  VECTOR v;
+  v.clear();
+  EXPECT_TRUE(v.empty());
+  v.resize(random(0U, 10000U));
+  auto c = v.capacity();
+  v.clear();
+  EXPECT_TRUE(v.empty());
+  EXPECT_EQ(v.capacity(), c);
+}
+
+BENCHMARK(BENCHFUN(zzInitRNG), iters) {
+  //LOG(INFO) << "\nTesting with type " << typeid(VECTOR).name() << "\n";
+  srand(seed);
+}
+
+BENCHMARK(BENCHFUN(defaultCtor), iters) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    VECTOR v[4096];
+    doNotOptimizeAway(&v);
+  }
+}
+
+void BENCHFUN(sizeCtor)(int iters, int size) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    VECTOR v(size);
+    doNotOptimizeAway(&v);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(sizeCtor), 128);
+BENCHMARK_PARAM(BENCHFUN(sizeCtor), 1024);
+BENCHMARK_PARAM(BENCHFUN(sizeCtor), 1048576);
+
+void BENCHFUN(fillCtor)(int iters, int size) {
+  FOR_EACH_RANGE (i, 0, iters) {
+    VECTOR v(size_t(size), randomObject<VECTOR::value_type>());
+    doNotOptimizeAway(&v);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(fillCtor), 128);
+BENCHMARK_PARAM(BENCHFUN(fillCtor), 1024);
+BENCHMARK_PARAM(BENCHFUN(fillCtor), 10240);
+
+void BENCHFUN(pushBack)(int iters, int size) {
+  auto const obj = randomObject<VECTOR::value_type>();
+  FOR_EACH_RANGE (i, 0, iters) {
+    VECTOR v;
+    FOR_EACH_RANGE (j, 0, size) {
+      v.push_back(obj);
+    }
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(pushBack), 128);
+BENCHMARK_PARAM(BENCHFUN(pushBack), 1024);
+BENCHMARK_PARAM(BENCHFUN(pushBack), 10240);
+BENCHMARK_PARAM(BENCHFUN(pushBack), 102400);
+BENCHMARK_PARAM(BENCHFUN(pushBack), 512000);
+
+void BENCHFUN(reserve)(int iters, int size) {
+  auto const obj = randomObject<VECTOR::value_type>();
+  VECTOR v(random(0U, 10000U), obj);
+  FOR_EACH_RANGE (i, 0, iters) {
+    v.reserve(random(0U, 100000U));
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(reserve), 128);
+BENCHMARK_PARAM(BENCHFUN(reserve), 1024);
+BENCHMARK_PARAM(BENCHFUN(reserve), 10240);
+
+void BENCHFUN(insert)(int iters, int size) {
+  auto const obj1 = randomObject<VECTOR::value_type>();
+  auto const obj2 = randomObject<VECTOR::value_type>();
+  VECTOR v(random(0U, 1U), obj1);
+  FOR_EACH_RANGE (i, 0, iters / 100) {
+    v.insert(v.begin(), obj2);
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(insert), 100);
+
+void BENCHFUN(erase)(int iters, int size) {
+  auto const obj1 = randomObject<VECTOR::value_type>();
+  VECTOR v(random(0U, 100U), obj1);
+  FOR_EACH_RANGE (i, 0, iters) {
+    if (v.empty()) continue;
+    v.erase(v.begin());
+  }
+}
+BENCHMARK_PARAM(BENCHFUN(erase), 1024);
diff --git a/faux-folly/folly/test/FileTestLockHelper.cpp b/faux-folly/folly/test/FileTestLockHelper.cpp
new file mode 100644
index 0000000..ab2520d
--- /dev/null
+++ b/faux-folly/folly/test/FileTestLockHelper.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 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 <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include <folly/File.h>
+
+DEFINE_bool(s, false, "get shared lock");
+DEFINE_bool(x, false, "get exclusive lock");
+
+int main(int argc, char *argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  google::InitGoogleLogging(argv[0]);
+  CHECK_EQ(FLAGS_s + FLAGS_x, 1)
+    << "exactly one of -s and -x must be specified";
+  CHECK_EQ(argc, 2);
+  folly::File f(argv[1], O_RDWR);
+  bool r;
+  if (FLAGS_s) {
+    r = f.try_lock_shared();
+  } else {
+    r = f.try_lock();
+  }
+  return r ? 0 : 42;
+}
diff --git a/faux-folly/folly/test/FileUtilTest.cpp b/faux-folly/folly/test/FileUtilTest.cpp
new file mode 100644
index 0000000..8fe88f0
--- /dev/null
+++ b/faux-folly/folly/test/FileUtilTest.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2015 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/FileUtil.h>
+#include <folly/detail/FileUtilDetail.h>
+
+#include <deque>
+
+#include <glog/logging.h>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+#include <folly/Range.h>
+#include <folly/String.h>
+
+namespace folly { namespace test {
+
+using namespace fileutil_detail;
+using namespace std;
+
+namespace {
+
+class Reader {
+ public:
+  Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec);
+
+  // write-like
+  ssize_t operator()(int fd, void* buf, size_t count);
+
+  // pwrite-like
+  ssize_t operator()(int fd, void* buf, size_t count, off_t offset);
+
+  // writev-like
+  ssize_t operator()(int fd, const iovec* iov, int count);
+
+  // pwritev-like
+  ssize_t operator()(int fd, const iovec* iov, int count, off_t offset);
+
+  const std::deque<ssize_t> spec() const { return spec_; }
+
+ private:
+  ssize_t nextSize();
+
+  off_t offset_;
+  StringPiece data_;
+  std::deque<ssize_t> spec_;
+};
+
+Reader::Reader(off_t offset, StringPiece data, std::deque<ssize_t> spec)
+  : offset_(offset),
+    data_(data),
+    spec_(std::move(spec)) {
+}
+
+ssize_t Reader::nextSize() {
+  if (spec_.empty()) {
+    throw std::runtime_error("spec empty");
+  }
+  ssize_t n = spec_.front();
+  spec_.pop_front();
+  if (n <= 0) {
+    if (n == -1) {
+      errno = EIO;
+    }
+    spec_.clear();  // so we fail if called again
+  } else {
+    offset_ += n;
+  }
+  return n;
+}
+
+ssize_t Reader::operator()(int fd, void* buf, size_t count) {
+  ssize_t n = nextSize();
+  if (n <= 0) {
+    return n;
+  }
+  if (size_t(n) > count) {
+    throw std::runtime_error("requested count too small");
+  }
+  memcpy(buf, data_.data(), n);
+  data_.advance(n);
+  return n;
+}
+
+ssize_t Reader::operator()(int fd, void* buf, size_t count, off_t offset) {
+  EXPECT_EQ(offset_, offset);
+  return operator()(fd, buf, count);
+}
+
+ssize_t Reader::operator()(int fd, const iovec* iov, int count) {
+  ssize_t n = nextSize();
+  if (n <= 0) {
+    return n;
+  }
+  ssize_t remaining = n;
+  for (; count != 0 && remaining != 0; ++iov, --count) {
+    ssize_t len = std::min(remaining, ssize_t(iov->iov_len));
+    memcpy(iov->iov_base, data_.data(), len);
+    data_.advance(len);
+    remaining -= len;
+  }
+  if (remaining != 0) {
+    throw std::runtime_error("requested total size too small");
+  }
+  return n;
+}
+
+ssize_t Reader::operator()(int fd, const iovec* iov, int count, off_t offset) {
+  EXPECT_EQ(offset_, offset);
+  return operator()(fd, iov, count);
+}
+
+}  // namespace
+
+class FileUtilTest : public ::testing::Test {
+ protected:
+  FileUtilTest();
+
+  Reader reader(std::deque<ssize_t> spec);
+
+  std::string in_;
+  std::vector<std::pair<size_t, Reader>> readers_;
+};
+
+FileUtilTest::FileUtilTest()
+  : in_("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
+  CHECK_EQ(62, in_.size());
+
+  readers_.emplace_back(0, reader({0}));
+  readers_.emplace_back(62, reader({62}));
+  readers_.emplace_back(62, reader({62, -1}));  // error after end (not called)
+  readers_.emplace_back(61, reader({61, 0}));
+  readers_.emplace_back(-1, reader({61, -1}));  // error before end
+  readers_.emplace_back(62, reader({31, 31}));
+  readers_.emplace_back(62, reader({1, 10, 20, 10, 1, 20}));
+  readers_.emplace_back(61, reader({1, 10, 20, 10, 20, 0}));
+  readers_.emplace_back(41, reader({1, 10, 20, 10, 0}));
+  readers_.emplace_back(-1, reader({1, 10, 20, 10, 20, -1}));
+}
+
+Reader FileUtilTest::reader(std::deque<ssize_t> spec) {
+  return Reader(42, in_, std::move(spec));
+}
+
+TEST_F(FileUtilTest, read) {
+  for (auto& p : readers_) {
+    std::string out(in_.size(), '\0');
+    EXPECT_EQ(p.first, wrapFull(p.second, 0, &out[0], out.size()));
+    if (p.first != (typeof(p.first))(-1)) {
+      EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
+    }
+  }
+}
+
+TEST_F(FileUtilTest, pread) {
+  for (auto& p : readers_) {
+    std::string out(in_.size(), '\0');
+    EXPECT_EQ(p.first, wrapFull(p.second, 0, &out[0], out.size(), off_t(42)));
+    if (p.first != (typeof(p.first))(-1)) {
+      EXPECT_EQ(in_.substr(0, p.first), out.substr(0, p.first));
+    }
+  }
+}
+
+class IovecBuffers {
+ public:
+  explicit IovecBuffers(std::initializer_list<size_t> sizes);
+
+  std::vector<iovec> iov() const { return iov_; }  // yes, make a copy
+  std::string join() const { return folly::join("", buffers_); }
+  size_t size() const;
+
+ private:
+  std::vector<std::string> buffers_;
+  std::vector<iovec> iov_;
+};
+
+IovecBuffers::IovecBuffers(std::initializer_list<size_t> sizes) {
+  iov_.reserve(sizes.size());
+  for (auto& s : sizes) {
+    buffers_.push_back(std::string(s, '\0'));
+  }
+  for (auto& b : buffers_) {
+    iovec iov;
+    iov.iov_base = &b[0];
+    iov.iov_len = b.size();
+    iov_.push_back(iov);
+  }
+}
+
+size_t IovecBuffers::size() const {
+  size_t s = 0;
+  for (auto& b : buffers_) {
+    s += b.size();
+  }
+  return s;
+}
+
+TEST_F(FileUtilTest, readv) {
+  for (auto& p : readers_) {
+    IovecBuffers buf({12, 19, 31});
+    ASSERT_EQ(62, buf.size());
+
+    auto iov = buf.iov();
+    EXPECT_EQ(p.first, wrapvFull(p.second, 0, iov.data(), iov.size()));
+    if (p.first != (typeof(p.first))(-1)) {
+      EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
+    }
+  }
+}
+
+#if FOLLY_HAVE_PREADV
+TEST_F(FileUtilTest, preadv) {
+  for (auto& p : readers_) {
+    IovecBuffers buf({12, 19, 31});
+    ASSERT_EQ(62, buf.size());
+
+    auto iov = buf.iov();
+    EXPECT_EQ(p.first,
+              wrapvFull(p.second, 0, iov.data(), iov.size(), off_t(42)));
+    if (p.first != (typeof(p.first))(-1)) {
+      EXPECT_EQ(in_.substr(0, p.first), buf.join().substr(0, p.first));
+    }
+  }
+}
+#endif
+
+TEST(String, readFile) {
+  srand(time(nullptr));
+  const string tmpPrefix = to<string>("/tmp/folly-file-util-test-",
+                                      getpid(), "-", rand(), "-");
+  const string afile = tmpPrefix + "myfile";
+  const string emptyFile = tmpPrefix + "myfile2";
+
+  SCOPE_EXIT {
+    unlink(afile.c_str());
+    unlink(emptyFile.c_str());
+  };
+
+  EXPECT_TRUE(writeFile(string(), emptyFile.c_str()));
+  EXPECT_TRUE(writeFile(StringPiece("bar"), afile.c_str()));
+
+  {
+    string contents;
+    EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
+    EXPECT_EQ(contents, "");
+    EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
+    EXPECT_EQ("", contents);
+    EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
+    EXPECT_EQ("ba", contents);
+    EXPECT_TRUE(readFile(afile.c_str(), contents));
+    EXPECT_EQ("bar", contents);
+  }
+  {
+    vector<unsigned char> contents;
+    EXPECT_TRUE(readFile(emptyFile.c_str(), contents));
+    EXPECT_EQ(vector<unsigned char>(), contents);
+    EXPECT_TRUE(readFile(afile.c_str(), contents, 0));
+    EXPECT_EQ(vector<unsigned char>(), contents);
+    EXPECT_TRUE(readFile(afile.c_str(), contents, 2));
+    EXPECT_EQ(vector<unsigned char>({'b', 'a'}), contents);
+    EXPECT_TRUE(readFile(afile.c_str(), contents));
+    EXPECT_EQ(vector<unsigned char>({'b', 'a', 'r'}), contents);
+  }
+}
+
+}}  // namespaces
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/ForeachTest.cpp b/faux-folly/folly/test/ForeachTest.cpp
new file mode 100644
index 0000000..4c13b93
--- /dev/null
+++ b/faux-folly/folly/test/ForeachTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+#include <map>
+#include <string>
+#include <vector>
+#include <list>
+
+using namespace folly;
+using namespace folly::detail;
+
+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, 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);
+}
+
+// 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.
+
+void setupBenchmark(size_t iters) {
+  bmMap.clear();
+  for (size_t i = 0; i < iters; ++i) {
+    bmMap[i] = "teststring";
+  }
+}
+
+BENCHMARK(ForEachKVNoMacroAssign, iters) {
+  int sumKeys = 0;
+  std::string sumValues;
+
+  BENCHMARK_SUSPEND {
+    setupBenchmark(iters);
+    int sumKeys = 0;
+    std::string sumValues = "";
+  }
+
+  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(ManualLoopNoAssign, iters) {
+  BENCHMARK_SUSPEND {
+    setupBenchmark(iters);
+  }
+  int sumKeys = 0;
+  std::string sumValues;
+
+  for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
+    sumKeys += iter->first;
+    sumValues += iter->second;
+  }
+}
+
+BENCHMARK(ForEachKVMacro, iters) {
+  BENCHMARK_SUSPEND {
+    setupBenchmark(iters);
+  }
+  int sumKeys = 0;
+  std::string sumValues;
+
+  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);
+  auto r = RUN_ALL_TESTS();
+  if (r) {
+    return r;
+  }
+  runBenchmarks();
+  return 0;
+}
diff --git a/faux-folly/folly/test/FormatTest.cpp b/faux-folly/folly/test/FormatTest.cpp
new file mode 100644
index 0000000..8676180
--- /dev/null
+++ b/faux-folly/folly/test/FormatTest.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2015 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/Format.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace folly;
+
+template <class Uint>
+void compareOctal(Uint u) {
+  char buf1[detail::kMaxOctalLength + 1];
+  buf1[detail::kMaxOctalLength] = '\0';
+  char* p = buf1 + detail::uintToOctal(buf1, detail::kMaxOctalLength, u);
+
+  char buf2[detail::kMaxOctalLength + 1];
+  EXPECT_LT(snprintf(buf2, sizeof(buf2), "%jo", static_cast<uintmax_t>(u)),
+            sizeof(buf2));
+
+  EXPECT_EQ(std::string(buf2), std::string(p));
+}
+
+template <class Uint>
+void compareHex(Uint u) {
+  char buf1[detail::kMaxHexLength + 1];
+  buf1[detail::kMaxHexLength] = '\0';
+  char* p = buf1 + detail::uintToHexLower(buf1, detail::kMaxHexLength, u);
+
+  char buf2[detail::kMaxHexLength + 1];
+  EXPECT_LT(snprintf(buf2, sizeof(buf2), "%jx", static_cast<uintmax_t>(u)),
+            sizeof(buf2));
+
+  EXPECT_EQ(std::string(buf2), std::string(p));
+}
+
+template <class Uint>
+void compareBinary(Uint u) {
+  char buf[detail::kMaxBinaryLength + 1];
+  buf[detail::kMaxBinaryLength] = '\0';
+  char* p = buf + detail::uintToBinary(buf, detail::kMaxBinaryLength, u);
+
+  std::string repr;
+  if (u == 0) {
+    repr = '0';
+  } else {
+    std::string tmp;
+    for (; u; u >>= 1) {
+      tmp.push_back(u & 1 ? '1' : '0');
+    }
+    repr.assign(tmp.rbegin(), tmp.rend());
+  }
+
+  EXPECT_EQ(repr, std::string(p));
+}
+
+TEST(Format, uintToOctal) {
+  for (unsigned i = 0; i < (1u << 16) + 2; i++) {
+    compareOctal(i);
+  }
+}
+
+TEST(Format, uintToHex) {
+  for (unsigned i = 0; i < (1u << 16) + 2; i++) {
+    compareHex(i);
+  }
+}
+
+TEST(Format, uintToBinary) {
+  for (unsigned i = 0; i < (1u << 16) + 2; i++) {
+    compareBinary(i);
+  }
+}
+
+TEST(Format, Simple) {
+  EXPECT_EQ("hello", sformat("hello"));
+  EXPECT_EQ("42", sformat("{}", 42));
+  EXPECT_EQ("42 42", sformat("{0} {0}", 42));
+  EXPECT_EQ("00042  23   42", sformat("{0:05} {1:3} {0:4}", 42, 23));
+  EXPECT_EQ("hello world hello 42",
+            sformat("{0} {1} {0} {2}", "hello", "world", 42));
+  EXPECT_EQ("XXhelloXX", sformat("{:X^9}", "hello"));
+  EXPECT_EQ("XXX42XXXX", sformat("{:X^9}", 42));
+  EXPECT_EQ("-0xYYYY2a", sformat("{:Y=#9x}", -42));
+  EXPECT_EQ("*", sformat("{}", '*'));
+  EXPECT_EQ("42", sformat("{}", 42));
+  EXPECT_EQ("0042", sformat("{:04}", 42));
+
+  EXPECT_EQ("hello  ", sformat("{:7}", "hello"));
+  EXPECT_EQ("hello  ", sformat("{:<7}", "hello"));
+  EXPECT_EQ("  hello", sformat("{:>7}", "hello"));
+
+  EXPECT_EQ("  hi", sformat("{:>*}", 4, "hi"));
+  EXPECT_EQ("   hi!", sformat("{:*}{}", 3, "", "hi!"));
+  EXPECT_EQ("    123", sformat("{:*}", 7, 123));
+  EXPECT_EQ("123    ", sformat("{:<*}", 7, 123));
+  EXPECT_EQ("----<=>----", sformat("{:-^*}", 11, "<=>"));
+  EXPECT_EQ("+++456+++", sformat("{2:+^*0}", 9, "unused", 456));
+
+  std::vector<int> v1 {10, 20, 30};
+  EXPECT_EQ("0020", sformat("{0[1]:04}", v1));
+  EXPECT_EQ("0020", svformat("{1:04}", v1));
+  EXPECT_EQ("10 20", svformat("{} {}", v1));
+
+  const std::vector<int> v2 = v1;
+  EXPECT_EQ("0020", sformat("{0[1]:04}", v2));
+  EXPECT_EQ("0020", svformat("{1:04}", v2));
+  EXPECT_THROW(sformat("{0[3]:04}", v2), std::out_of_range);
+  EXPECT_THROW(svformat("{3:04}", v2), std::out_of_range);
+  EXPECT_EQ("0020", sformat("{0[1]:04}", defaulted(v2, 42)));
+  EXPECT_EQ("0020", svformat("{1:04}", defaulted(v2, 42)));
+  EXPECT_EQ("0042", sformat("{0[3]:04}", defaulted(v2, 42)));
+  EXPECT_EQ("0042", svformat("{3:04}", defaulted(v2, 42)));
+
+  const int p[] = {10, 20, 30};
+  const int* q = p;
+  EXPECT_EQ("0020", sformat("{0[1]:04}", p));
+  EXPECT_EQ("0020", svformat("{1:04}", p));
+  EXPECT_EQ("0020", sformat("{0[1]:04}", q));
+  EXPECT_EQ("0020", svformat("{1:04}", q));
+  EXPECT_NE("", sformat("{}", q));
+
+  EXPECT_EQ("0x", sformat("{}", p).substr(0, 2));
+  EXPECT_EQ("10", svformat("{}", p));
+  EXPECT_EQ("0x", sformat("{}", q).substr(0, 2));
+  EXPECT_EQ("10", svformat("{}", q));
+  q = nullptr;
+  EXPECT_EQ("(null)", sformat("{}", q));
+
+  std::map<int, std::string> m { {10, "hello"}, {20, "world"} };
+  EXPECT_EQ("worldXX", sformat("{[20]:X<7}", m));
+  EXPECT_EQ("worldXX", svformat("{20:X<7}", m));
+  EXPECT_THROW(sformat("{[42]:X<7}", m), std::out_of_range);
+  EXPECT_THROW(svformat("{42:X<7}", m), std::out_of_range);
+  EXPECT_EQ("worldXX", sformat("{[20]:X<7}", defaulted(m, "meow")));
+  EXPECT_EQ("worldXX", svformat("{20:X<7}", defaulted(m, "meow")));
+  EXPECT_EQ("meowXXX", sformat("{[42]:X<7}", defaulted(m, "meow")));
+  EXPECT_EQ("meowXXX", svformat("{42:X<7}", defaulted(m, "meow")));
+
+  std::map<std::string, std::string> m2 { {"hello", "world"} };
+  EXPECT_EQ("worldXX", sformat("{[hello]:X<7}", m2));
+  EXPECT_EQ("worldXX", svformat("{hello:X<7}", m2));
+  EXPECT_THROW(sformat("{[none]:X<7}", m2), std::out_of_range);
+  EXPECT_THROW(svformat("{none:X<7}", m2), std::out_of_range);
+  EXPECT_EQ("worldXX", sformat("{[hello]:X<7}", defaulted(m2, "meow")));
+  EXPECT_EQ("worldXX", svformat("{hello:X<7}", defaulted(m2, "meow")));
+  EXPECT_EQ("meowXXX", sformat("{[none]:X<7}", defaulted(m2, "meow")));
+  EXPECT_EQ("meowXXX", svformat("{none:X<7}", defaulted(m2, "meow")));
+
+  // Test indexing in strings
+  EXPECT_EQ("61 62", sformat("{0[0]:x} {0[1]:x}", "abcde"));
+  EXPECT_EQ("61 62", svformat("{0:x} {1:x}", "abcde"));
+  EXPECT_EQ("61 62", sformat("{0[0]:x} {0[1]:x}", std::string("abcde")));
+  EXPECT_EQ("61 62", svformat("{0:x} {1:x}", std::string("abcde")));
+
+  // Test booleans
+  EXPECT_EQ("true", sformat("{}", true));
+  EXPECT_EQ("1", sformat("{:d}", true));
+  EXPECT_EQ("false", sformat("{}", false));
+  EXPECT_EQ("0", sformat("{:d}", false));
+
+  // Test pairs
+  {
+    std::pair<int, std::string> p {42, "hello"};
+    EXPECT_EQ("    42 hello ", sformat("{0[0]:6} {0[1]:6}", p));
+    EXPECT_EQ("    42 hello ", svformat("{:6} {:6}", p));
+  }
+
+  // Test tuples
+  {
+    std::tuple<int, std::string, int> t { 42, "hello", 23 };
+    EXPECT_EQ("    42 hello      23", sformat("{0[0]:6} {0[1]:6} {0[2]:6}", t));
+    EXPECT_EQ("    42 hello      23", svformat("{:6} {:6} {:6}", t));
+  }
+
+  // Test writing to stream
+  std::ostringstream os;
+  os << format("{} {}", 42, 23);
+  EXPECT_EQ("42 23", os.str());
+
+  // Test appending to string
+  std::string s;
+  format(&s, "{} {}", 42, 23);
+  format(&s, " hello {:X<7}", "world");
+  EXPECT_EQ("42 23 hello worldXX", s);
+}
+
+TEST(Format, Float) {
+  double d = 1;
+  EXPECT_EQ("1", sformat("{}", 1.0));
+  EXPECT_EQ("0.1", sformat("{}", 0.1));
+  EXPECT_EQ("0.01", sformat("{}", 0.01));
+  EXPECT_EQ("0.001", sformat("{}", 0.001));
+  EXPECT_EQ("0.0001", sformat("{}", 0.0001));
+  EXPECT_EQ("1e-5", sformat("{}", 0.00001));
+  EXPECT_EQ("1e-6", sformat("{}", 0.000001));
+
+  EXPECT_EQ("10", sformat("{}", 10.0));
+  EXPECT_EQ("100", sformat("{}", 100.0));
+  EXPECT_EQ("1000", sformat("{}", 1000.0));
+  EXPECT_EQ("10000", sformat("{}", 10000.0));
+  EXPECT_EQ("100000", sformat("{}", 100000.0));
+  EXPECT_EQ("1e+6", sformat("{}", 1000000.0));
+  EXPECT_EQ("1e+7", sformat("{}", 10000000.0));
+
+  EXPECT_EQ("1.00", sformat("{:.2f}", 1.0));
+  EXPECT_EQ("0.10", sformat("{:.2f}", 0.1));
+  EXPECT_EQ("0.01", sformat("{:.2f}", 0.01));
+  EXPECT_EQ("0.00", sformat("{:.2f}", 0.001));
+
+  EXPECT_EQ("100000. !== 100000", sformat("{:.} !== {:.}", 100000.0, 100000));
+  EXPECT_EQ("100000.", sformat("{:.}", 100000.0));
+  EXPECT_EQ("1e+6", sformat("{:.}", 1000000.0));
+  EXPECT_EQ(" 100000.", sformat("{:8.}", 100000.0));
+  EXPECT_EQ("100000.", sformat("{:4.}", 100000.0));
+  EXPECT_EQ("  100000", sformat("{:8.8}", 100000.0));
+  EXPECT_EQ(" 100000.", sformat("{:8.8.}", 100000.0));
+}
+
+TEST(Format, MultiLevel) {
+  std::vector<std::map<std::string, std::string>> v = {
+    {
+      {"hello", "world"},
+    },
+  };
+
+  EXPECT_EQ("world", sformat("{[0.hello]}", v));
+}
+
+TEST(Format, separatorDecimalInteger) {
+  EXPECT_EQ("0", sformat("{:,d}", 0));
+  EXPECT_EQ("1", sformat("{:d}", 1));
+  EXPECT_EQ("1", sformat("{:,d}", 1));
+  EXPECT_EQ("1", sformat("{:,}", 1));
+  EXPECT_EQ("123", sformat("{:d}", 123));
+  EXPECT_EQ("123", sformat("{:,d}", 123));
+  EXPECT_EQ("123", sformat("{:,}", 123));
+  EXPECT_EQ("1234", sformat("{:d}", 1234));
+  EXPECT_EQ("1,234", sformat("{:,d}", 1234));
+  EXPECT_EQ("1,234", sformat("{:,}", 1234));
+  EXPECT_EQ("12345678", sformat("{:d}", 12345678));
+  EXPECT_EQ("12,345,678", sformat("{:,d}", 12345678));
+  EXPECT_EQ("12,345,678", sformat("{:,}", 12345678));
+  EXPECT_EQ("-1234", sformat("{:d}", -1234));
+  EXPECT_EQ("-1,234", sformat("{:,d}", -1234));
+  EXPECT_EQ("-1,234", sformat("{:,}", -1234));
+
+  int64_t max_int64_t = std::numeric_limits<int64_t>::max();
+  int64_t min_int64_t = std::numeric_limits<int64_t>::min();
+  uint64_t max_uint64_t = std::numeric_limits<uint64_t>::max();
+  EXPECT_EQ("9223372036854775807", sformat("{:d}", max_int64_t));
+  EXPECT_EQ("9,223,372,036,854,775,807", sformat("{:,d}", max_int64_t));
+  EXPECT_EQ("9,223,372,036,854,775,807", sformat("{:,}", max_int64_t));
+  EXPECT_EQ("-9223372036854775808", sformat("{:d}", min_int64_t));
+  EXPECT_EQ("-9,223,372,036,854,775,808", sformat("{:,d}", min_int64_t));
+  EXPECT_EQ("-9,223,372,036,854,775,808", sformat("{:,}", min_int64_t));
+  EXPECT_EQ("18446744073709551615", sformat("{:d}", max_uint64_t));
+  EXPECT_EQ("18,446,744,073,709,551,615", sformat("{:,d}", max_uint64_t));
+  EXPECT_EQ("18,446,744,073,709,551,615", sformat("{:,}", max_uint64_t));
+
+  EXPECT_EQ("  -1,234", sformat("{: 8,}", -1234));
+  EXPECT_EQ("-001,234", sformat("{:08,d}", -1234));
+  EXPECT_EQ("-00001,234", sformat("{:010,d}", -1234));
+  EXPECT_EQ(" -1,234 ", sformat("{:^ 8,d}", -1234));
+}
+
+// Note that sformat("{:n}", ...) uses the current locale setting to insert the
+// appropriate number separator characters.
+TEST(Format, separatorNumber) {
+  EXPECT_EQ("0", sformat("{:n}", 0));
+  EXPECT_EQ("1", sformat("{:n}", 1));
+  EXPECT_EQ("123", sformat("{:n}", 123));
+  EXPECT_EQ("1234", sformat("{:n}", 1234));
+  EXPECT_EQ("12345678", sformat("{:n}", 12345678));
+  EXPECT_EQ("-1234", sformat("{:n}", -1234));
+
+  int64_t max_int64_t = std::numeric_limits<int64_t>::max();
+  int64_t min_int64_t = std::numeric_limits<int64_t>::min();
+  uint64_t max_uint64_t = std::numeric_limits<uint64_t>::max();
+  EXPECT_EQ("9223372036854775807", sformat("{:n}", max_int64_t));
+  EXPECT_EQ("-9223372036854775808", sformat("{:n}", min_int64_t));
+  EXPECT_EQ("18446744073709551615", sformat("{:n}", max_uint64_t));
+
+  EXPECT_EQ("   -1234", sformat("{: 8n}", -1234));
+  EXPECT_EQ("-0001234", sformat("{:08n}", -1234));
+  EXPECT_EQ("-000001234", sformat("{:010n}", -1234));
+  EXPECT_EQ(" -1234  ", sformat("{:^ 8n}", -1234));
+}
+
+// insertThousandsGroupingUnsafe requires non-const params
+static void testGrouping(const char* a_str, const char* expected) {
+  char str[256];
+  char* end_ptr = str + snprintf(str, sizeof(str), "%s", a_str);
+  ASSERT_LT(end_ptr, str + sizeof(str));
+  folly::detail::insertThousandsGroupingUnsafe(str, &end_ptr);
+  ASSERT_STREQ(expected, str);
+}
+
+TEST(Format, separatorUnit) {
+  testGrouping("0", "0");
+  testGrouping("1", "1");
+  testGrouping("12", "12");
+  testGrouping("123", "123");
+  testGrouping("1234", "1,234");
+  testGrouping("12345", "12,345");
+  testGrouping("123456", "123,456");
+  testGrouping("1234567", "1,234,567");
+  testGrouping("1234567890", "1,234,567,890");
+  testGrouping("9223372036854775807", "9,223,372,036,854,775,807");
+  testGrouping("18446744073709551615", "18,446,744,073,709,551,615");
+}
+
+
+namespace {
+
+struct KeyValue {
+  std::string key;
+  int value;
+};
+
+}  // namespace
+
+namespace folly {
+
+template <> class FormatValue<KeyValue> {
+ public:
+  explicit FormatValue(const KeyValue& kv) : kv_(kv) { }
+
+  template <class FormatCallback>
+  void format(FormatArg& arg, FormatCallback& cb) const {
+    format_value::formatFormatter(
+        folly::format("<key={}, value={}>", kv_.key, kv_.value),
+        arg, cb);
+  }
+
+ private:
+  const KeyValue& kv_;
+};
+
+}  // namespace
+
+TEST(Format, Custom) {
+  KeyValue kv { "hello", 42 };
+
+  EXPECT_EQ("<key=hello, value=42>", sformat("{}", kv));
+  EXPECT_EQ("<key=hello, value=42>", sformat("{:10}", kv));
+  EXPECT_EQ("<key=hello", sformat("{:.10}", kv));
+  EXPECT_EQ("<key=hello, value=42>XX", sformat("{:X<23}", kv));
+  EXPECT_EQ("XX<key=hello, value=42>", sformat("{:X>23}", kv));
+  EXPECT_EQ("<key=hello, value=42>", sformat("{0[0]}", &kv));
+  EXPECT_NE("", sformat("{}", &kv));
+}
+
+namespace {
+
+struct Opaque {
+  int k;
+};
+
+} // namespace
+
+#define EXPECT_THROW_STR(code, type, str) \
+  do { \
+    bool caught = false; \
+    try { \
+      code; \
+    } catch (const type& e) { \
+      caught = true; \
+      EXPECT_TRUE(strstr(e.what(), (str)) != nullptr) << \
+        "Expected message [" << (str) << "], actual message [" << \
+        e.what(); \
+    } catch (const std::exception& e) { \
+      caught = true; \
+      ADD_FAILURE() << "Caught different exception type; expected " #type \
+        ", caught " << folly::demangle(typeid(e)); \
+    } catch (...) { \
+      caught = true; \
+      ADD_FAILURE() << "Caught unknown exception type; expected " #type; \
+    } \
+    if (!caught) { \
+      ADD_FAILURE() << "Expected exception " #type ", caught nothing"; \
+    } \
+  } while (false)
+
+#define EXPECT_FORMAT_ERROR(code, str) \
+  EXPECT_THROW_STR(code, folly::BadFormatArg, (str))
+
+TEST(Format, Unformatted) {
+  Opaque o;
+  EXPECT_NE("", sformat("{}", &o));
+  EXPECT_FORMAT_ERROR(sformat("{0[0]}", &o),
+                      "No formatter available for this type");
+}
+
+TEST(Format, Nested) {
+  EXPECT_EQ("1 2 3 4", sformat("{} {} {}", 1, 2, format("{} {}", 3, 4)));
+  //
+  // not copyable, must hold temporary in scope instead.
+  auto&& saved = format("{} {}", 3, 4);
+  EXPECT_EQ("1 2 3 4", sformat("{} {} {}", 1, 2, saved));
+}
+
+TEST(Format, OutOfBounds) {
+  std::vector<int> ints{1, 2, 3, 4, 5};
+  EXPECT_EQ("1 3 5", sformat("{0[0]} {0[2]} {0[4]}", ints));
+  EXPECT_THROW(sformat("{[5]}", ints), std::out_of_range);
+
+  std::map<std::string, int> map{{"hello", 0}, {"world", 1}};
+  EXPECT_EQ("hello = 0", sformat("hello = {[hello]}", map));
+  EXPECT_THROW(sformat("{[nope]}", map), std::out_of_range);
+  EXPECT_THROW(svformat("{nope}", map), std::out_of_range);
+}
+
+TEST(Format, BogusFormatString) {
+  EXPECT_FORMAT_ERROR(sformat("}"), "single '}' in format string");
+  EXPECT_FORMAT_ERROR(sformat("foo}bar"), "single '}' in format string");
+  EXPECT_FORMAT_ERROR(sformat("foo{bar"), "missing ending '}'");
+  EXPECT_FORMAT_ERROR(sformat("{[test]"), "missing ending '}'");
+  EXPECT_FORMAT_ERROR(sformat("{-1.3}"), "argument index must be non-negative");
+  EXPECT_FORMAT_ERROR(sformat("{1.3}", 0, 1, 2), "index not allowed");
+  EXPECT_FORMAT_ERROR(sformat("{0} {} {1}", 0, 1, 2),
+               "may not have both default and explicit arg indexes");
+  EXPECT_FORMAT_ERROR(sformat("{:*}", 1.2),
+                      "dynamic field width argument must be integral");
+  EXPECT_FORMAT_ERROR(sformat("{} {:*}", "hi"),
+                      "argument index out of range, max=1");
+  EXPECT_FORMAT_ERROR(
+    sformat("{:*0}", 12, "ok"),
+    "cannot provide width arg index without value arg index"
+  );
+  EXPECT_FORMAT_ERROR(
+    sformat("{0:*}", 12, "ok"),
+    "cannot provide value arg index without width arg index"
+  );
+
+  std::vector<int> v{1, 2, 3};
+  EXPECT_FORMAT_ERROR(svformat("{:*}", v),
+                      "dynamic field width not supported in vformat()");
+
+  // This one fails in detail::enforceWhitespace(), which throws
+  // std::range_error
+  EXPECT_THROW_STR(sformat("{0[test}"), std::range_error,
+                   "Non-whitespace: [");
+}
+
+template <bool containerMode, class... Args>
+class TestExtendingFormatter;
+
+template <bool containerMode, class... Args>
+class TestExtendingFormatter
+    : public BaseFormatter<TestExtendingFormatter<containerMode, Args...>,
+                           containerMode,
+                           Args...> {
+ private:
+  explicit TestExtendingFormatter(StringPiece& str, Args&&... args)
+      : BaseFormatter<TestExtendingFormatter<containerMode, Args...>,
+                      containerMode,
+                      Args...>(str, std::forward<Args>(args)...) {}
+
+  template <size_t K, class Callback>
+  void doFormatArg(FormatArg& arg, Callback& cb) const {
+    std::string result;
+    auto appender = [&result](StringPiece s) {
+      result.append(s.data(), s.size());
+    };
+    std::get<K>(this->values_).format(arg, appender);
+    result = sformat("{{{}}}", result);
+    cb(StringPiece(result));
+  }
+
+  friend class BaseFormatter<TestExtendingFormatter<containerMode, Args...>,
+                             containerMode,
+                             Args...>;
+
+  template <class... A>
+  friend std::string texsformat(StringPiece fmt, A&&... arg);
+};
+
+template <class... Args>
+std::string texsformat(StringPiece fmt, Args&&... args) {
+  return TestExtendingFormatter<false, Args...>(
+      fmt, std::forward<Args>(args)...).str();
+}
+
+TEST(Format, Extending) {
+  EXPECT_EQ(texsformat("I {} brackets", "love"), "I {love} brackets");
+  EXPECT_EQ(texsformat("I {} nesting", sformat("really {}", "love")),
+            "I {really love} nesting");
+  EXPECT_EQ(
+      sformat("I also {} nesting", texsformat("have an {} for", "affinity")),
+      "I also have an {affinity} for nesting");
+  EXPECT_EQ(texsformat("Extending {} in {}",
+                       texsformat("a {}", "formatter"),
+                       "another formatter"),
+            "Extending {a {formatter}} in {another formatter}");
+}
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/FutexTest.cpp b/faux-folly/folly/test/FutexTest.cpp
new file mode 100644
index 0000000..d1f4bff
--- /dev/null
+++ b/faux-folly/folly/test/FutexTest.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015 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/detail/Futex.h>
+#include <folly/test/DeterministicSchedule.h>
+
+#include <chrono>
+#include <functional>
+#include <ratio>
+#include <thread>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include <time.h>
+
+using namespace folly::detail;
+using namespace folly::test;
+using namespace std;
+using namespace std::chrono;
+
+typedef DeterministicSchedule DSched;
+
+template <template<typename> class Atom>
+void run_basic_thread(
+    Futex<Atom>& f) {
+  EXPECT_TRUE(f.futexWait(0));
+}
+
+template <template<typename> class Atom>
+void run_basic_tests() {
+  Futex<Atom> f(0);
+
+  EXPECT_FALSE(f.futexWait(1));
+  EXPECT_EQ(f.futexWake(), 0);
+
+  auto thr = DSched::thread(std::bind(run_basic_thread<Atom>, std::ref(f)));
+
+  while (f.futexWake() != 1) {
+    std::this_thread::yield();
+  }
+
+  DSched::join(thr);
+}
+
+template <template<typename> class Atom, typename Clock, typename Duration>
+void liveClockWaitUntilTests() {
+  Futex<Atom> f(0);
+
+  /* FYI: 100 iterations in the AARCH64 qemu emulator takes about 6.5 minutes */
+  for (int stress = 0; stress < 100 ; ++stress) {
+    auto fp = &f; // workaround for t5336595
+    auto thrA = DSched::thread([fp,stress]{
+      while (true) {
+        const auto deadline = time_point_cast<Duration>(
+            Clock::now() + microseconds(1 << (stress % 20)));
+        const auto res = fp->futexWaitUntil(0, deadline);
+        EXPECT_TRUE(res == FutexResult::TIMEDOUT || res == FutexResult::AWOKEN);
+        if (res == FutexResult::AWOKEN) {
+          break;
+        }
+      }
+    });
+
+    while (f.futexWake() != 1) {
+      std::this_thread::yield();
+    }
+
+    DSched::join(thrA);
+  }
+
+  {
+    const auto start = Clock::now();
+    const auto deadline = time_point_cast<Duration>(start + milliseconds(100));
+    EXPECT_EQ(f.futexWaitUntil(0, deadline), FutexResult::TIMEDOUT);
+    LOG(INFO) << "Futex wait timed out after waiting for "
+              << duration_cast<milliseconds>(Clock::now() - start).count()
+              << "ms using clock with " << Duration::period::den
+              << " precision, should be ~100ms";
+  }
+
+  {
+    const auto start = Clock::now();
+    const auto deadline = time_point_cast<Duration>(
+        start - 2 * start.time_since_epoch());
+    EXPECT_EQ(f.futexWaitUntil(0, deadline), FutexResult::TIMEDOUT);
+    LOG(INFO) << "Futex wait with invalid deadline timed out after waiting for "
+              << duration_cast<milliseconds>(Clock::now() - start).count()
+              << "ms using clock with " << Duration::period::den
+              << " precision, should be ~0ms";
+  }
+}
+
+template <typename Clock>
+void deterministicAtomicWaitUntilTests() {
+  Futex<DeterministicAtomic> f(0);
+
+  // Futex wait must eventually fail with either FutexResult::TIMEDOUT or
+  // FutexResult::INTERRUPTED
+  const auto res = f.futexWaitUntil(0, Clock::now() + milliseconds(100));
+  EXPECT_TRUE(res == FutexResult::TIMEDOUT || res == FutexResult::INTERRUPTED);
+}
+
+template<template<typename> class Atom>
+void run_wait_until_tests() {
+  liveClockWaitUntilTests<Atom, system_clock, system_clock::duration>();
+  liveClockWaitUntilTests<Atom, steady_clock, steady_clock::duration>();
+
+  typedef duration<int64_t, std::ratio<1, 10000000>> decimicroseconds;
+  liveClockWaitUntilTests<Atom, system_clock, decimicroseconds>();
+}
+
+template <>
+void run_wait_until_tests<DeterministicAtomic>() {
+  deterministicAtomicWaitUntilTests<system_clock>();
+  deterministicAtomicWaitUntilTests<steady_clock>();
+}
+
+uint64_t diff(uint64_t a, uint64_t b) {
+  return a > b ? a - b : b - a;
+}
+
+void run_system_clock_test() {
+  /* Test to verify that system_clock uses clock_gettime(CLOCK_REALTIME, ...)
+   * for the time_points */
+  struct timespec ts;
+  const int maxIters = 1000;
+  int iter = 0;
+  const uint64_t delta = 10000000 /* 10 ms */;
+
+  /** The following loop is only to make the test more robust in the presence of
+   * clock adjustments that can occur. We just run the loop maxIter times and
+   * expect with very high probability that there will be atleast one iteration
+   * of the test during which clock adjustments > delta have not occurred. */
+  while (iter < maxIters) {
+    uint64_t a = duration_cast<nanoseconds>(system_clock::now()
+                                            .time_since_epoch()).count();
+
+    clock_gettime(CLOCK_REALTIME, &ts);
+    uint64_t b = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+    uint64_t c = duration_cast<nanoseconds>(system_clock::now()
+                                            .time_since_epoch()).count();
+
+    if (diff(a, b) <= delta &&
+        diff(b, c) <= delta &&
+        diff(a, c) <= 2 * delta) {
+      /* Success! system_clock uses CLOCK_REALTIME for time_points */
+      break;
+    }
+    iter++;
+  }
+  EXPECT_TRUE(iter < maxIters);
+}
+
+void run_steady_clock_test() {
+  /* Test to verify that steady_clock uses clock_gettime(CLOCK_MONOTONIC, ...)
+   * for the time_points */
+  EXPECT_TRUE(steady_clock::is_steady);
+
+  const uint64_t A = duration_cast<nanoseconds>(steady_clock::now()
+                                                .time_since_epoch()).count();
+
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  const uint64_t B = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+  const uint64_t C = duration_cast<nanoseconds>(steady_clock::now()
+                                                .time_since_epoch()).count();
+  EXPECT_TRUE(A <= B && B <= C);
+}
+
+TEST(Futex, clock_source) {
+  run_system_clock_test();
+
+  /* On some systems steady_clock is just an alias for system_clock. So,
+   * we must skip run_steady_clock_test if the two clocks are the same. */
+  if (!std::is_same<system_clock,steady_clock>::value) {
+    run_steady_clock_test();
+  }
+}
+
+TEST(Futex, basic_live) {
+  run_basic_tests<std::atomic>();
+  run_wait_until_tests<std::atomic>();
+}
+
+TEST(Futex, basic_emulated) {
+  run_basic_tests<EmulatedFutexAtomic>();
+  run_wait_until_tests<EmulatedFutexAtomic>();
+}
+
+TEST(Futex, basic_deterministic) {
+  DSched sched(DSched::uniform(0));
+  run_basic_tests<DeterministicAtomic>();
+  run_wait_until_tests<DeterministicAtomic>();
+}
+
+int main(int argc, char ** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/HasMemberFnTraitsTest.cpp b/faux-folly/folly/test/HasMemberFnTraitsTest.cpp
new file mode 100644
index 0000000..8ed1040
--- /dev/null
+++ b/faux-folly/folly/test/HasMemberFnTraitsTest.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+
+#include <folly/Traits.h>
+
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+
+#include <string>
+
+using namespace std;
+using namespace folly;
+
+FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test, test);
+
+struct Foo {
+  int test();
+  int test() const;
+  string test(const string&) const;
+};
+
+struct Bar {
+  int test();
+  double test(int,long);
+  long test(int) const;
+};
+
+struct Gaz {
+  void test();
+  void test() const;
+  void test() /* nolint */ volatile;
+  void test() const /* nolint */ volatile;
+};
+
+struct NoCV {
+  void test();
+};
+
+struct Const {
+  void test() const;
+};
+
+struct Volatile {
+  void test() /* nolint */ volatile;
+};
+
+struct CV {
+  void test() const /* nolint */ volatile;
+};
+
+bool log_value(const char* what, bool result) {
+  LOG(INFO) << what << ": " << boolalpha << result;
+  return result;
+}
+
+#define LOG_VALUE(x) log_value(#x, x)
+
+TEST(HasMemberFnTraits, DirectMembers) {
+  EXPECT_TRUE(LOG_VALUE((has_test<Foo, int()>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Foo, int() const>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Foo, double(int, long)>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Foo, string(const string&) const>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Foo, long(int) const>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Foo, string(string) const>::value)));
+
+  EXPECT_TRUE(LOG_VALUE((has_test<Bar, int()>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Bar, int() const>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Bar, double(int, long)>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Bar, string(const string&) const>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Bar, long(int) const>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Bar, string(string) const>::value)));
+
+  EXPECT_TRUE(LOG_VALUE((has_test<Gaz, void()>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Gaz, void() const>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Gaz, void() /* nolint */ volatile>::value)));
+  EXPECT_TRUE(LOG_VALUE((
+          has_test<Gaz, void() const /* nolint */ volatile>::value)));
+  EXPECT_TRUE(LOG_VALUE((
+          has_test<Gaz, void() /* nolint */ volatile const>::value)));
+
+  EXPECT_TRUE(LOG_VALUE((has_test<NoCV, void()>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<NoCV, void() const>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<NoCV, void() /* nolint */ volatile>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<NoCV, void() const /* nolint */ volatile>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<NoCV, void() /* nolint */ volatile const>::value)));
+
+  EXPECT_FALSE(LOG_VALUE((has_test<Const, void()>::value)));
+  EXPECT_TRUE(LOG_VALUE((has_test<Const, void() const>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<Const, void() /* nolint */ volatile>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<Const, void() const /* nolint */ volatile>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<Const, void() /* nolint */ volatile const>::value)));
+
+  EXPECT_FALSE(LOG_VALUE((has_test<Volatile, void()>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<Volatile, void() const>::value)));
+  EXPECT_TRUE(LOG_VALUE((
+          has_test<Volatile, void() /* nolint */ volatile>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<Volatile, void() const /* nolint */ volatile>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<Volatile, void() /* nolint */ volatile const>::value)));
+
+  EXPECT_FALSE(LOG_VALUE((has_test<CV, void()>::value)));
+  EXPECT_FALSE(LOG_VALUE((has_test<CV, void() const>::value)));
+  EXPECT_FALSE(LOG_VALUE((
+          has_test<CV, void() /* nolint */ volatile>::value)));
+  EXPECT_TRUE(LOG_VALUE((
+          has_test<CV, void() const /* nolint */ volatile>::value)));
+  EXPECT_TRUE(LOG_VALUE((
+          has_test<CV, void() /* nolint */ volatile const>::value)));
+}
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/HashTest.cpp b/faux-folly/folly/test/HashTest.cpp
new file mode 100644
index 0000000..bbae4f6
--- /dev/null
+++ b/faux-folly/folly/test/HashTest.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2015 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/Hash.h>
+#include <folly/MapUtil.h>
+#include <gtest/gtest.h>
+#include <stdint.h>
+#include <unordered_map>
+#include <utility>
+
+using namespace folly::hash;
+
+TEST(Hash, Fnv32) {
+  const char* s1 = "hello, world!";
+  const uint32_t s1_res = 3605494790UL;
+  EXPECT_EQ(fnv32(s1), s1_res);
+  EXPECT_EQ(fnv32(s1), fnv32_buf(s1, strlen(s1)));
+
+  const char* s2 = "monkeys! m0nk3yz! ev3ry \\/\\/here~~~~";
+  const uint32_t s2_res = 1270448334UL;
+  EXPECT_EQ(fnv32(s2), s2_res);
+  EXPECT_EQ(fnv32(s2), fnv32_buf(s2, strlen(s2)));
+
+  const char* s3 = "";
+  const uint32_t s3_res = 2166136261UL;
+  EXPECT_EQ(fnv32(s3), s3_res);
+  EXPECT_EQ(fnv32(s3), fnv32_buf(s3, strlen(s3)));
+}
+
+TEST(Hash, Fnv64) {
+  const char* s1 = "hello, world!";
+  const uint64_t s1_res = 13991426986746681734ULL;
+  EXPECT_EQ(fnv64(s1), s1_res);
+  EXPECT_EQ(fnv64(s1), fnv64_buf(s1, strlen(s1)));
+
+  const char* s2 = "monkeys! m0nk3yz! ev3ry \\/\\/here~~~~";
+  const uint64_t s2_res = 6091394665637302478ULL;
+  EXPECT_EQ(fnv64(s2), s2_res);
+  EXPECT_EQ(fnv64(s2), fnv64_buf(s2, strlen(s2)));
+
+  const char* s3 = "";
+  const uint64_t s3_res = 14695981039346656037ULL;
+  EXPECT_EQ(fnv64(s3), s3_res);
+  EXPECT_EQ(fnv64(s3), fnv64_buf(s3, strlen(s3)));
+
+  // note: Use fnv64_buf to make a single hash value from multiple
+  // fields/datatypes.
+  const char* t4_a = "E Pluribus";
+  int64_t t4_b = 0xF1E2D3C4B5A69788;
+  int32_t t4_c = 0xAB12CD34;
+  const char* t4_d = "Unum";
+  uint64_t t4_res = 15571330457339273965ULL;
+  uint64_t t4_hash1 = fnv64_buf(t4_a,
+                                strlen(t4_a));
+  uint64_t t4_hash2 = fnv64_buf(reinterpret_cast<void*>(&t4_b),
+                                sizeof(int64_t),
+                                t4_hash1);
+  uint64_t t4_hash3 = fnv64_buf(reinterpret_cast<void*>(&t4_c),
+                                sizeof(int32_t),
+                                t4_hash2);
+  uint64_t t4_hash4 = fnv64_buf(t4_d,
+                                strlen(t4_d),
+                                t4_hash3);
+  EXPECT_EQ(t4_hash4, t4_res);
+  // note: These are probabalistic, not determinate, but c'mon.
+  // These hash values should be different, or something's not
+  // working.
+  EXPECT_NE(t4_hash1, t4_hash4);
+  EXPECT_NE(t4_hash2, t4_hash4);
+  EXPECT_NE(t4_hash3, t4_hash4);
+}
+
+TEST(Hash, Hsieh32) {
+  const char* s1 = "hello, world!";
+  const uint32_t s1_res = 2918802987ul;
+  EXPECT_EQ(hsieh_hash32(s1), s1_res);
+  EXPECT_EQ(hsieh_hash32(s1), hsieh_hash32_buf(s1, strlen(s1)));
+
+  const char* s2 = "monkeys! m0nk3yz! ev3ry \\/\\/here~~~~";
+  const uint32_t s2_res = 47373213ul;
+  EXPECT_EQ(hsieh_hash32(s2), s2_res);
+  EXPECT_EQ(hsieh_hash32(s2), hsieh_hash32_buf(s2, strlen(s2)));
+
+  const char* s3 = "";
+  const uint32_t s3_res = 0;
+  EXPECT_EQ(hsieh_hash32(s3), s3_res);
+  EXPECT_EQ(hsieh_hash32(s3), hsieh_hash32_buf(s3, strlen(s3)));
+}
+
+TEST(Hash, TWang_Mix64) {
+  uint64_t i1 = 0x78a87873e2d31dafULL;
+  uint64_t i1_res = 3389151152926383528ULL;
+  EXPECT_EQ(i1_res, twang_mix64(i1));
+  EXPECT_EQ(i1, twang_unmix64(i1_res));
+
+  uint64_t i2 = 0x0123456789abcdefULL;
+  uint64_t i2_res = 3061460455458984563ull;
+  EXPECT_EQ(i2_res, twang_mix64(i2));
+  EXPECT_EQ(i2, twang_unmix64(i2_res));
+}
+
+namespace {
+void checkTWang(uint64_t r) {
+  uint64_t result = twang_mix64(r);
+  EXPECT_EQ(r, twang_unmix64(result));
+}
+}  // namespace
+
+TEST(Hash, TWang_Unmix64) {
+  // We'll try (1 << i), (1 << i) + 1, (1 << i) - 1
+  for (int i = 1; i < 64; i++) {
+    checkTWang((1U << i) - 1);
+    checkTWang(1U << i);
+    checkTWang((1U << i) + 1);
+  }
+}
+
+TEST(Hash, TWang_32From64) {
+  uint64_t i1 = 0x78a87873e2d31dafULL;
+  uint32_t i1_res = 1525586863ul;
+  EXPECT_EQ(twang_32from64(i1), i1_res);
+
+  uint64_t i2 = 0x0123456789abcdefULL;
+  uint32_t i2_res = 2918899159ul;
+  EXPECT_EQ(twang_32from64(i2), i2_res);
+}
+
+TEST(Hash, Jenkins_Rev_Mix32) {
+  uint32_t i1 = 3805486511ul;
+  uint32_t i1_res = 381808021ul;
+  EXPECT_EQ(i1_res, jenkins_rev_mix32(i1));
+  EXPECT_EQ(i1, jenkins_rev_unmix32(i1_res));
+
+  uint32_t i2 = 2309737967ul;
+  uint32_t i2_res = 1834777923ul;
+  EXPECT_EQ(i2_res, jenkins_rev_mix32(i2));
+  EXPECT_EQ(i2, jenkins_rev_unmix32(i2_res));
+}
+
+namespace {
+void checkJenkins(uint32_t r) {
+  uint32_t result = jenkins_rev_mix32(r);
+  EXPECT_EQ(r, jenkins_rev_unmix32(result));
+}
+}  // namespace
+
+TEST(Hash, Jenkins_Rev_Unmix32) {
+  // We'll try (1 << i), (1 << i) + 1, (1 << i) - 1
+  for (int i = 1; i < 32; i++) {
+    checkJenkins((1U << i) - 1);
+    checkJenkins(1U << i);
+    checkJenkins((1U << i) + 1);
+  }
+}
+
+TEST(Hash, hasher) {
+  // Basically just confirms that things compile ok.
+  std::unordered_map<int32_t,int32_t,folly::hasher<int32_t>> m;
+  m.insert(std::make_pair(4, 5));
+  EXPECT_EQ(get_default(m, 4), 5);
+}
+
+// Not a full hasher since only handles one type
+class TestHasher {
+ public:
+  static size_t hash(const std::pair<int, int>& p) {
+    return p.first + p.second;
+  }
+};
+
+template <typename T, typename... Ts>
+size_t hash_combine_test(const T& t, const Ts&... ts) {
+  return hash_combine_generic<TestHasher>(t, ts...);
+}
+
+TEST(Hash, pair) {
+  auto a = std::make_pair(1, 2);
+  auto b = std::make_pair(3, 4);
+  auto c = std::make_pair(1, 2);
+  auto d = std::make_pair(2, 1);
+  EXPECT_EQ(hash_combine(a),
+            hash_combine(c));
+  EXPECT_NE(hash_combine(b),
+            hash_combine(c));
+  EXPECT_NE(hash_combine(d),
+            hash_combine(c));
+
+  // With composition
+  EXPECT_EQ(hash_combine(a, b),
+            hash_combine(c, b));
+  // Test order dependence
+  EXPECT_NE(hash_combine(a, b),
+            hash_combine(b, a));
+
+  // Test with custom hasher
+  EXPECT_EQ(hash_combine_test(a),
+            hash_combine_test(c));
+  // 3 + 4 != 1 + 2
+  EXPECT_NE(hash_combine_test(b),
+            hash_combine_test(c));
+  // This time, thanks to a terrible hash function, these are equal
+  EXPECT_EQ(hash_combine_test(d),
+            hash_combine_test(c));
+  // With composition
+  EXPECT_EQ(hash_combine_test(a, b),
+            hash_combine_test(c, b));
+  // Test order dependence
+  EXPECT_NE(hash_combine_test(a, b),
+            hash_combine_test(b, a));
+  // Again, 1 + 2 == 2 + 1
+  EXPECT_EQ(hash_combine_test(a, b),
+            hash_combine_test(d, b));
+}
+
+TEST(Hash, hash_combine) {
+  EXPECT_NE(hash_combine(1, 2), hash_combine(2, 1));
+}
+
+TEST(Hash, std_tuple) {
+  typedef std::tuple<int64_t, std::string, int32_t> tuple3;
+  tuple3 t(42, "foo", 1);
+
+  std::unordered_map<tuple3, std::string> m;
+  m[t] = "bar";
+  EXPECT_EQ("bar", m[t]);
+}
+
+TEST(Hash, enum_type) {
+  const auto hash = folly::Hash();
+
+  enum class Enum32 : int32_t { Foo, Bar };
+  EXPECT_EQ(hash(static_cast<int32_t>(Enum32::Foo)), hash(Enum32::Foo));
+  EXPECT_EQ(hash(static_cast<int32_t>(Enum32::Bar)), hash(Enum32::Bar));
+  EXPECT_NE(hash(Enum32::Foo), hash(Enum32::Bar));
+
+  std::unordered_map<Enum32, std::string, folly::Hash> m32;
+  m32[Enum32::Foo] = "foo";
+  EXPECT_EQ("foo", m32[Enum32::Foo]);
+
+  enum class Enum64 : int64_t { Foo, Bar };
+  EXPECT_EQ(hash(static_cast<int64_t>(Enum64::Foo)), hash(Enum64::Foo));
+  EXPECT_EQ(hash(static_cast<int64_t>(Enum64::Bar)), hash(Enum64::Bar));
+  EXPECT_NE(hash(Enum64::Foo), hash(Enum64::Bar));
+
+  std::unordered_map<Enum64, std::string, folly::Hash> m64;
+  m64[Enum64::Foo] = "foo";
+  EXPECT_EQ("foo", m64[Enum64::Foo]);
+}
+
+TEST(Hash, pair_folly_hash) {
+  typedef std::pair<int64_t, int32_t> pair2;
+  pair2 p(42, 1);
+
+  std::unordered_map<pair2, std::string, folly::Hash> m;
+  m[p] = "bar";
+  EXPECT_EQ("bar", m[p]);
+}
+
+TEST(Hash, tuple_folly_hash) {
+  typedef std::tuple<int64_t, int32_t, int32_t> tuple3;
+  tuple3 t(42, 1, 1);
+
+  std::unordered_map<tuple3, std::string, folly::Hash> m;
+  m[t] = "bar";
+  EXPECT_EQ("bar", m[t]);
+}
+
+namespace {
+template <class T>
+size_t hash_vector(const std::vector<T>& v) {
+  return hash_range(v.begin(), v.end());
+}
+}
+
+TEST(Hash, hash_range) {
+  EXPECT_EQ(hash_vector<int32_t>({1, 2}), hash_vector<int16_t>({1, 2}));
+  EXPECT_NE(hash_vector<int>({2, 1}), hash_vector<int>({1, 2}));
+  EXPECT_EQ(hash_vector<int>({}), hash_vector<float>({}));
+}
+
+TEST(Hash, std_tuple_different_hash) {
+  typedef std::tuple<int64_t, std::string, int32_t> tuple3;
+  tuple3 t1(42, "foo", 1);
+  tuple3 t2(9, "bar", 3);
+  tuple3 t3(42, "foo", 3);
+
+  EXPECT_NE(std::hash<tuple3>()(t1),
+            std::hash<tuple3>()(t2));
+  EXPECT_NE(std::hash<tuple3>()(t1),
+            std::hash<tuple3>()(t3));
+}
+
+TEST(Range, Hash) {
+  using namespace folly;
+
+  StringPiece a1 = "10050517", b1 = "51107032",
+              a2 = "10050518", b2 = "51107033",
+              a3 = "10050519", b3 = "51107034",
+              a4 = "10050525", b4 = "51107040";
+  Range<const wchar_t*> w1 = range(L"10050517"), w2 = range(L"51107032"),
+                        w3 = range(L"10050518"), w4 = range(L"51107033");
+  StringPieceHash h1;
+  Hash h2;
+  EXPECT_EQ(h1(a1), h1(b1));
+  EXPECT_EQ(h1(a2), h1(b2));
+  EXPECT_EQ(h1(a3), h1(b3));
+  EXPECT_EQ(h1(a4), h1(b4));
+  EXPECT_NE(h2(a1), h2(b1));
+  EXPECT_NE(h2(a1), h2(b1));
+  EXPECT_NE(h2(a2), h2(b2));
+  EXPECT_NE(h2(a3), h2(b3));
+  EXPECT_NE(h2(ByteRange(a1)), h2(ByteRange(b1)));
+  EXPECT_NE(h2(ByteRange(a2)), h2(ByteRange(b2)));
+  EXPECT_NE(h2(ByteRange(a3)), h2(ByteRange(b3)));
+  EXPECT_NE(h2(ByteRange(a4)), h2(ByteRange(b4)));
+  EXPECT_NE(h2(w1), h2(w2));
+  EXPECT_NE(h2(w1), h2(w3));
+  EXPECT_NE(h2(w2), h2(w4));
+}
diff --git a/faux-folly/folly/test/HistogramBenchmark.cpp b/faux-folly/folly/test/HistogramBenchmark.cpp
new file mode 100644
index 0000000..7307828
--- /dev/null
+++ b/faux-folly/folly/test/HistogramBenchmark.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 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/stats/Histogram.h>
+
+#include <gflags/gflags.h>
+
+#include <folly/Benchmark.h>
+#include <folly/Foreach.h>
+
+using folly::Histogram;
+
+void addValue(unsigned int n, int64_t bucketSize, int64_t min, int64_t max) {
+  Histogram<int64_t> hist(bucketSize, min, max);
+  int64_t num = min;
+  FOR_EACH_RANGE (i, 0, n) {
+    hist.addValue(num);
+    ++num;
+    if (num > max) { num = min; }
+  }
+}
+
+BENCHMARK_NAMED_PARAM(addValue, 0_to_100, 1, 0, 100);
+BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000);
+BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000);
+
+int main(int argc, char *argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
diff --git a/faux-folly/folly/test/HistogramTest.cpp b/faux-folly/folly/test/HistogramTest.cpp
new file mode 100644
index 0000000..fac9cc1
--- /dev/null
+++ b/faux-folly/folly/test/HistogramTest.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2015 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/stats/Histogram.h>
+#include <folly/stats/Histogram-defs.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using folly::Histogram;
+
+// Insert 100 evenly distributed values into a histogram with 100 buckets
+TEST(Histogram, Test100) {
+  Histogram<int64_t> h(1, 0, 100);
+
+  for (unsigned int n = 0; n < 100; ++n) {
+    h.addValue(n);
+  }
+
+  // 100 buckets, plus 1 for below min, and 1 for above max
+  EXPECT_EQ(h.getNumBuckets(), 102);
+
+  double epsilon = 1e-6;
+  for (unsigned int n = 0; n <= 100; ++n) {
+    double pct = n / 100.0;
+
+    // Floating point arithmetic isn't 100% accurate, and if we just divide
+    // (n / 100) the value should be exactly on a bucket boundary.  Add espilon
+    // to ensure we fall in the upper bucket.
+    if (n < 100) {
+      double lowPct = -1.0;
+      double highPct = -1.0;
+      unsigned int bucketIdx = h.getPercentileBucketIdx(pct + epsilon,
+                                                        &lowPct, &highPct);
+      EXPECT_EQ(n + 1, bucketIdx);
+      EXPECT_FLOAT_EQ(n / 100.0, lowPct);
+      EXPECT_FLOAT_EQ((n + 1) / 100.0, highPct);
+    }
+
+    // Also test n - epsilon, to test falling in the lower bucket.
+    if (n > 0) {
+      double lowPct = -1.0;
+      double highPct = -1.0;
+      unsigned int bucketIdx = h.getPercentileBucketIdx(pct - epsilon,
+                                                        &lowPct, &highPct);
+      EXPECT_EQ(n, bucketIdx);
+      EXPECT_FLOAT_EQ((n - 1) / 100.0, lowPct);
+      EXPECT_FLOAT_EQ(n / 100.0, highPct);
+    }
+
+    // Check getPercentileEstimate()
+    EXPECT_EQ(n, h.getPercentileEstimate(pct));
+  }
+}
+
+// Test calling getPercentileBucketIdx() and getPercentileEstimate() on an
+// empty histogram
+TEST(Histogram, TestEmpty) {
+  Histogram<int64_t> h(1, 0, 100);
+
+  for (unsigned int n = 0; n <= 100; ++n) {
+    double pct = n / 100.0;
+
+    double lowPct = -1.0;
+    double highPct = -1.0;
+    unsigned int bucketIdx = h.getPercentileBucketIdx(pct, &lowPct, &highPct);
+    EXPECT_EQ(1, bucketIdx);
+    EXPECT_FLOAT_EQ(0.0, lowPct);
+    EXPECT_FLOAT_EQ(0.0, highPct);
+
+    EXPECT_EQ(0, h.getPercentileEstimate(pct));
+  }
+}
+
+// Test calling getPercentileBucketIdx() and getPercentileEstimate() on a
+// histogram with just a single value.
+TEST(Histogram, Test1) {
+  Histogram<int64_t> h(1, 0, 100);
+  h.addValue(42);
+
+  for (unsigned int n = 0; n < 100; ++n) {
+    double pct = n / 100.0;
+
+    double lowPct = -1.0;
+    double highPct = -1.0;
+    unsigned int bucketIdx = h.getPercentileBucketIdx(pct, &lowPct, &highPct);
+    EXPECT_EQ(43, bucketIdx);
+    EXPECT_FLOAT_EQ(0.0, lowPct);
+    EXPECT_FLOAT_EQ(1.0, highPct);
+
+    EXPECT_EQ(42, h.getPercentileEstimate(pct));
+  }
+}
+
+// Test adding enough numbers to make the sum value overflow in the
+// "below min" bucket
+TEST(Histogram, TestOverflowMin) {
+  Histogram<int64_t> h(1, 0, 100);
+
+  for (unsigned int n = 0; n < 9; ++n) {
+    h.addValue(-0x0fffffffffffffff);
+  }
+
+  // Compute a percentile estimate.  We only added values to the "below min"
+  // bucket, so this should check that bucket.  We're mainly verifying that the
+  // code doesn't crash here when the bucket average is larger than the max
+  // value that is supposed to be in the bucket.
+  int64_t estimate = h.getPercentileEstimate(0.05);
+  // The code will return the smallest possible value when it detects an
+  // overflow beyond the minimum value.
+  EXPECT_EQ(std::numeric_limits<int64_t>::min(), estimate);
+}
+
+// Test adding enough numbers to make the sum value overflow in the
+// "above max" bucket
+TEST(Histogram, TestOverflowMax) {
+  Histogram<int64_t> h(1, 0, 100);
+
+  for (unsigned int n = 0; n < 9; ++n) {
+    h.addValue(0x0fffffffffffffff);
+  }
+
+  // The code will return the maximum possible value when it detects an
+  // overflow beyond the max value.
+  int64_t estimate = h.getPercentileEstimate(0.95);
+  EXPECT_EQ(std::numeric_limits<int64_t>::max(), estimate);
+}
+
+// Test adding enough numbers to make the sum value overflow in one of the
+// normal buckets
+TEST(Histogram, TestOverflowBucket) {
+  Histogram<int64_t> h(0x0100000000000000, 0, 0x1000000000000000);
+
+  for (unsigned int n = 0; n < 9; ++n) {
+    h.addValue(0x0fffffffffffffff);
+  }
+
+  // The histogram code should return the bucket midpoint
+  // when it detects overflow.
+  int64_t estimate = h.getPercentileEstimate(0.95);
+  EXPECT_EQ(0x0f80000000000000, estimate);
+}
+
+TEST(Histogram, TestDouble) {
+  // Insert 100 evenly spaced values into a histogram
+  Histogram<double> h(100.0, 0.0, 5000.0);
+  for (double n = 50; n < 5000; n += 100) {
+    h.addValue(n);
+  }
+  EXPECT_EQ(52, h.getNumBuckets());
+  EXPECT_EQ(2500.0, h.getPercentileEstimate(0.5));
+  EXPECT_EQ(4500.0, h.getPercentileEstimate(0.9));
+}
+
+// Test where the bucket width is not an even multiple of the histogram range
+TEST(Histogram, TestDoubleInexactWidth) {
+  Histogram<double> h(100.0, 0.0, 4970.0);
+  for (double n = 50; n < 5000; n += 100) {
+    h.addValue(n);
+  }
+  EXPECT_EQ(52, h.getNumBuckets());
+  EXPECT_EQ(2500.0, h.getPercentileEstimate(0.5));
+  EXPECT_EQ(4500.0, h.getPercentileEstimate(0.9));
+
+  EXPECT_EQ(0, h.getBucketByIndex(51).count);
+  h.addValue(4990);
+  h.addValue(5100);
+  EXPECT_EQ(2, h.getBucketByIndex(51).count);
+  EXPECT_EQ(2600.0, h.getPercentileEstimate(0.5));
+}
+
+// Test where the bucket width is larger than the histogram range
+// (There isn't really much point to defining a histogram this way,
+// but we want to ensure that it still works just in case.)
+TEST(Histogram, TestDoubleWidthTooBig) {
+  Histogram<double> h(100.0, 0.0, 7.0);
+  EXPECT_EQ(3, h.getNumBuckets());
+
+  for (double n = 0; n < 7; n += 1) {
+    h.addValue(n);
+  }
+  EXPECT_EQ(0, h.getBucketByIndex(0).count);
+  EXPECT_EQ(7, h.getBucketByIndex(1).count);
+  EXPECT_EQ(0, h.getBucketByIndex(2).count);
+  EXPECT_EQ(3.0, h.getPercentileEstimate(0.5));
+
+  h.addValue(-1.0);
+  EXPECT_EQ(1, h.getBucketByIndex(0).count);
+  h.addValue(7.5);
+  EXPECT_EQ(1, h.getBucketByIndex(2).count);
+  EXPECT_EQ(3.0, h.getPercentileEstimate(0.5));
+}
+
+// Test that we get counts right
+TEST(Histogram, Counts) {
+  Histogram<int32_t> h(1, 0, 10);
+  EXPECT_EQ(12, h.getNumBuckets());
+  EXPECT_EQ(0, h.computeTotalCount());
+
+  // Add one to each bucket, make sure the counts match
+  for (int32_t i = 0; i < 10; i++) {
+    h.addValue(i);
+    EXPECT_EQ(i+1, h.computeTotalCount());
+  }
+
+  // Add a lot to one bucket, make sure the counts still make sense
+  for (int32_t i = 0; i < 100; i++) {
+    h.addValue(0);
+  }
+  EXPECT_EQ(110, h.computeTotalCount());
+}
diff --git a/faux-folly/folly/test/IndexedMemPoolTest.cpp b/faux-folly/folly/test/IndexedMemPoolTest.cpp
new file mode 100644
index 0000000..b8e9fce
--- /dev/null
+++ b/faux-folly/folly/test/IndexedMemPoolTest.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2015 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/IndexedMemPool.h>
+#include <folly/test/DeterministicSchedule.h>
+#include <string>
+#include <thread>
+#include <unistd.h>
+#include <semaphore.h>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+using namespace folly::test;
+
+TEST(IndexedMemPool, unique_ptr) {
+  typedef IndexedMemPool<size_t> Pool;
+  Pool pool(100);
+
+  for (size_t i = 0; i < 100000; ++i) {
+    auto ptr = pool.allocElem();
+    EXPECT_TRUE(!!ptr);
+    *ptr = i;
+  }
+
+  std::vector<Pool::UniquePtr> leak;
+  while (true) {
+    auto ptr = pool.allocElem();
+    if (!ptr) {
+      // good, we finally ran out
+      break;
+    }
+    leak.emplace_back(std::move(ptr));
+    EXPECT_LT(leak.size(), 10000);
+  }
+}
+
+TEST(IndexedMemPool, no_starvation) {
+  const int count = 1000;
+  const int poolSize = 100;
+
+  typedef DeterministicSchedule Sched;
+  Sched sched(Sched::uniform(0));
+
+  typedef IndexedMemPool<int,8,8,DeterministicAtomic> Pool;
+  Pool pool(poolSize);
+
+  for (auto pass = 0; pass < 10; ++pass) {
+    int fd[2];
+    EXPECT_EQ(pipe(fd), 0);
+
+    // makes sure we wait for available nodes, rather than fail allocIndex
+    sem_t allocSem;
+    sem_init(&allocSem, 0, poolSize);
+
+    // this semaphore is only needed for deterministic replay, so that we
+    // always block in an Sched:: operation rather than in a read() syscall
+    sem_t readSem;
+    sem_init(&readSem, 0, 0);
+
+    std::thread produce = Sched::thread([&]() {
+      for (auto i = 0; i < count; ++i) {
+        Sched::wait(&allocSem);
+        uint32_t idx = pool.allocIndex();
+        EXPECT_NE(idx, 0);
+        EXPECT_LE(idx,
+            poolSize + (pool.NumLocalLists - 1) * pool.LocalListLimit);
+        pool[idx] = i;
+        EXPECT_EQ(write(fd[1], &idx, sizeof(idx)), sizeof(idx));
+        Sched::post(&readSem);
+      }
+    });
+
+    std::thread consume = Sched::thread([&]() {
+      for (auto i = 0; i < count; ++i) {
+        uint32_t idx;
+        Sched::wait(&readSem);
+        EXPECT_EQ(read(fd[0], &idx, sizeof(idx)), sizeof(idx));
+        EXPECT_NE(idx, 0);
+        EXPECT_GE(idx, 1);
+        EXPECT_LE(idx,
+            poolSize + (Pool::NumLocalLists - 1) * Pool::LocalListLimit);
+        EXPECT_EQ(pool[idx], i);
+        pool.recycleIndex(idx);
+        Sched::post(&allocSem);
+      }
+    });
+
+    Sched::join(produce);
+    Sched::join(consume);
+    close(fd[0]);
+    close(fd[1]);
+  }
+}
+
+TEST(IndexedMemPool, st_capacity) {
+  // only one local list => capacity is exact
+  typedef IndexedMemPool<int,1,32> Pool;
+  Pool pool(10);
+
+  EXPECT_EQ(pool.capacity(), 10);
+  EXPECT_EQ(Pool::maxIndexForCapacity(10), 10);
+  for (auto i = 0; i < 10; ++i) {
+    EXPECT_NE(pool.allocIndex(), 0);
+  }
+  EXPECT_EQ(pool.allocIndex(), 0);
+}
+
+TEST(IndexedMemPool, mt_capacity) {
+  typedef IndexedMemPool<int,16,32> Pool;
+  Pool pool(1000);
+
+  std::thread threads[10];
+  for (auto i = 0; i < 10; ++i) {
+    threads[i] = std::thread([&]() {
+      for (auto j = 0; j < 100; ++j) {
+        uint32_t idx = pool.allocIndex();
+        EXPECT_NE(idx, 0);
+      }
+    });
+  }
+
+  for (auto i = 0; i < 10; ++i) {
+    threads[i].join();
+  }
+
+  for (auto i = 0; i < 16 * 32; ++i) {
+    pool.allocIndex();
+  }
+  EXPECT_EQ(pool.allocIndex(), 0);
+}
+
+TEST(IndexedMemPool, locate_elem) {
+  IndexedMemPool<int> pool(1000);
+
+  for (auto i = 0; i < 1000; ++i) {
+    auto idx = pool.allocIndex();
+    EXPECT_FALSE(idx == 0);
+    int* elem = &pool[idx];
+    EXPECT_TRUE(idx == pool.locateElem(elem));
+  }
+
+  EXPECT_EQ(pool.locateElem(nullptr), 0);
+}
+
+struct NonTrivialStruct {
+  static __thread int count;
+
+  int elem_;
+
+  NonTrivialStruct() {
+    elem_ = 0;
+    ++count;
+  }
+
+  NonTrivialStruct(std::unique_ptr<std::string>&& arg1, int arg2) {
+    elem_ = arg1->length() + arg2;
+    ++count;
+  }
+
+  ~NonTrivialStruct() {
+    --count;
+  }
+};
+
+__thread int NonTrivialStruct::count;
+
+TEST(IndexedMemPool, eager_recycle) {
+  typedef IndexedMemPool<NonTrivialStruct> Pool;
+  Pool pool(100);
+
+  EXPECT_EQ(NonTrivialStruct::count, 0);
+
+  for (size_t i = 0; i < 10; ++i) {
+    {
+      std::unique_ptr<std::string> arg{ new std::string{ "abc" } };
+      auto ptr = pool.allocElem(std::move(arg), 100);
+      EXPECT_EQ(NonTrivialStruct::count, 1);
+      EXPECT_EQ(ptr->elem_, 103);
+      EXPECT_TRUE(!!ptr);
+    }
+    EXPECT_EQ(NonTrivialStruct::count, 0);
+  }
+}
+
+TEST(IndexedMemPool, late_recycle) {
+  {
+    typedef IndexedMemPool<NonTrivialStruct, 8, 8, std::atomic, false, false>
+        Pool;
+    Pool pool(100);
+
+    EXPECT_EQ(NonTrivialStruct::count, 0);
+
+    for (size_t i = 0; i < 10; ++i) {
+      {
+        auto ptr = pool.allocElem();
+        EXPECT_TRUE(NonTrivialStruct::count > 0);
+        EXPECT_TRUE(!!ptr);
+        ptr->elem_ = i;
+      }
+      EXPECT_TRUE(NonTrivialStruct::count > 0);
+    }
+  }
+  EXPECT_EQ(NonTrivialStruct::count, 0);
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/LazyTest.cpp b/faux-folly/folly/test/LazyTest.cpp
new file mode 100644
index 0000000..5ce3ff0
--- /dev/null
+++ b/faux-folly/folly/test/LazyTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 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/Lazy.h>
+
+#include <map>
+#include <functional>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+namespace folly {
+
+TEST(Lazy, Simple) {
+  int computeCount = 0;
+
+  auto const val = folly::lazy([&]() -> int {
+    ++computeCount;
+    EXPECT_EQ(computeCount, 1);
+    return 12;
+  });
+  EXPECT_EQ(computeCount, 0);
+
+  for (int i = 0; i < 100; ++i) {
+    if (i > 50) {
+      EXPECT_EQ(val(), 12);
+      EXPECT_EQ(computeCount, 1);
+    } else {
+      EXPECT_EQ(computeCount, 0);
+    }
+  }
+  EXPECT_EQ(val(), 12);
+  EXPECT_EQ(computeCount, 1);
+}
+
+auto globalCount = folly::lazy([]{ return 0; });
+auto const foo = folly::lazy([]() -> std::string {
+  ++globalCount();
+  EXPECT_EQ(globalCount(), 1);
+  return std::string("YEP");
+});
+
+TEST(Lazy, Global) {
+  EXPECT_EQ(globalCount(), 0);
+  EXPECT_EQ(foo(), "YEP");
+  EXPECT_EQ(globalCount(), 1);
+}
+
+TEST(Lazy, Map) {
+  auto lazyMap = folly::lazy([]() -> std::map<std::string,std::string> {
+    return {
+      { "foo", "bar" },
+      { "baz", "quux" }
+    };
+  });
+
+  EXPECT_EQ(lazyMap().size(), 2);
+  lazyMap()["blah"] = "asd";
+  EXPECT_EQ(lazyMap().size(), 3);
+}
+
+struct CopyCount {
+  CopyCount() {}
+  CopyCount(const CopyCount&) { ++count; }
+  CopyCount(CopyCount&&) noexcept {}
+
+  static int count;
+
+  bool operator()() const { return true ; }
+};
+
+int CopyCount::count = 0;
+
+TEST(Lazy, NonLambda) {
+  auto const rval = folly::lazy(CopyCount());
+  EXPECT_EQ(CopyCount::count, 0);
+  EXPECT_EQ(rval(), true);
+  EXPECT_EQ(CopyCount::count, 0);
+
+  CopyCount cpy;
+  auto const lval = folly::lazy(cpy);
+  EXPECT_EQ(CopyCount::count, 1);
+  EXPECT_EQ(lval(), true);
+  EXPECT_EQ(CopyCount::count, 1);
+
+  std::function<bool()> f = [&]{ return 12; };
+  auto const lazyF = folly::lazy(f);
+  EXPECT_EQ(lazyF(), true);
+}
+
+TEST(Lazy, Consty) {
+  std::function<int ()> const f = [&] { return 12; };
+  auto lz = folly::lazy(f);
+  EXPECT_EQ(lz(), 12);
+}
+
+}
diff --git a/faux-folly/folly/test/LifoSemTests.cpp b/faux-folly/folly/test/LifoSemTests.cpp
new file mode 100644
index 0000000..85e67c8
--- /dev/null
+++ b/faux-folly/folly/test/LifoSemTests.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2015 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/LifoSem.h>
+#include <folly/test/DeterministicSchedule.h>
+
+#include <thread>
+#include <semaphore.h>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+#include <folly/Random.h>
+
+using namespace folly;
+using namespace folly::test;
+
+typedef LifoSemImpl<DeterministicAtomic> DLifoSem;
+typedef DeterministicSchedule DSched;
+
+LIFOSEM_DECLARE_POOL(DeterministicAtomic, 100000)
+
+TEST(LifoSem, basic) {
+  LifoSem sem;
+  EXPECT_FALSE(sem.tryWait());
+  sem.post();
+  EXPECT_TRUE(sem.tryWait());
+  sem.post();
+  sem.wait();
+}
+
+TEST(LifoSem, multi) {
+  LifoSem sem;
+
+  const int opsPerThread = 10000;
+  std::thread threads[10];
+  std::atomic<int> blocks(0);
+
+  for (auto& thr : threads) {
+    thr = std::thread([&]{
+      int b = 0;
+      for (int i = 0; i < opsPerThread; ++i) {
+        if (!sem.tryWait()) {
+          sem.wait();
+          ++b;
+        }
+        sem.post();
+      }
+      blocks += b;
+    });
+  }
+
+  // start the flood
+  sem.post();
+
+  for (auto& thr : threads) {
+    thr.join();
+  }
+
+  LOG(INFO) << opsPerThread * sizeof(threads)/sizeof(threads[0])
+            << " post/wait pairs, " << blocks << " blocked";
+}
+
+TEST(LifoSem, pingpong) {
+  DSched sched(DSched::uniform(0));
+
+  const int iters = 100;
+
+  for (int pass = 0; pass < 10; ++pass) {
+    DLifoSem a;
+    DLifoSem b;
+
+    auto thr = DSched::thread([&]{
+      for (int i = 0; i < iters; ++i) {
+        a.wait();
+        // main thread can't be running here
+        EXPECT_EQ(a.valueGuess(), 0);
+        EXPECT_EQ(b.valueGuess(), 0);
+        b.post();
+      }
+    });
+    for (int i = 0; i < iters; ++i) {
+      a.post();
+      b.wait();
+      // child thread can't be running here
+      EXPECT_EQ(a.valueGuess(), 0);
+      EXPECT_EQ(b.valueGuess(), 0);
+    }
+    DSched::join(thr);
+  }
+}
+
+TEST(LifoSem, mutex) {
+  DSched sched(DSched::uniform(0));
+
+  const int iters = 100;
+
+  for (int pass = 0; pass < 10; ++pass) {
+    DLifoSem a;
+
+    auto thr = DSched::thread([&]{
+      for (int i = 0; i < iters; ++i) {
+        a.wait();
+        a.post();
+      }
+    });
+    for (int i = 0; i < iters; ++i) {
+      a.post();
+      a.wait();
+    }
+    a.post();
+    DSched::join(thr);
+    a.wait();
+  }
+}
+
+TEST(LifoSem, no_blocking) {
+  long seed = folly::randomNumberSeed() % 10000;
+  LOG(INFO) << "seed=" << seed;
+  DSched sched(DSched::uniform(seed));
+
+  const int iters = 100;
+  const int numThreads = 2;
+  const int width = 10;
+
+  for (int pass = 0; pass < 10; ++pass) {
+    DLifoSem a;
+
+    std::vector<std::thread> threads;
+    while (threads.size() < numThreads) {
+      threads.emplace_back(DSched::thread([&]{
+        for (int i = 0; i < iters; ++i) {
+          a.post(width);
+          for (int w = 0; w < width; ++w) {
+            a.wait();
+          }
+        }
+      }));
+    }
+    for (auto& thr : threads) {
+      DSched::join(thr);
+    }
+  }
+}
+
+TEST(LifoSem, one_way) {
+  long seed = folly::randomNumberSeed() % 10000;
+  LOG(INFO) << "seed=" << seed;
+  DSched sched(DSched::uniformSubset(seed, 1, 6));
+
+  const int iters = 1000;
+
+  for (int pass = 0; pass < 10; ++pass) {
+    DLifoSem a;
+
+    auto thr = DSched::thread([&]{
+      for (int i = 0; i < iters; ++i) {
+        a.wait();
+      }
+    });
+    for (int i = 0; i < iters; ++i) {
+      a.post();
+    }
+    DSched::join(thr);
+  }
+}
+
+TEST(LifoSem, shutdown_race) {
+  long seed = folly::randomNumberSeed() % 10000;
+  LOG(INFO) << "seed=" << seed;
+
+  bool shutdownWon = false;
+  bool shutdownLost = false;
+  for (int pass = 0; pass < 1000; ++pass) {
+    DSched sched(DSched::uniformSubset(seed + pass, 1, 1 + (pass % 50)));
+
+    DLifoSem a;
+    int waitCount = 0;
+    auto thr = DSched::thread([&]{
+      try {
+        while (true) {
+          a.wait();
+          ++waitCount;
+        }
+      } catch (ShutdownSemError& x) {
+        // expected
+        EXPECT_TRUE(a.isShutdown());
+      }
+    });
+    EXPECT_TRUE(!a.isShutdown());
+    a.shutdown();
+    EXPECT_TRUE(a.isShutdown());
+    a.post();
+    DSched::join(thr);
+    EXPECT_EQ(1, waitCount + a.valueGuess());
+    if (waitCount == 0) {
+      shutdownWon = true;
+    } else {
+      shutdownLost = true;
+    }
+  }
+  EXPECT_TRUE(shutdownWon);
+  EXPECT_TRUE(shutdownLost);
+}
+
+TEST(LifoSem, shutdown_multi) {
+  DSched sched(DSched::uniform(0));
+
+  for (int pass = 0; pass < 10; ++pass) {
+    DLifoSem a;
+    std::vector<std::thread> threads;
+    while (threads.size() < 20) {
+      threads.push_back(DSched::thread([&]{
+        try {
+          a.wait();
+          EXPECT_TRUE(false);
+        } catch (ShutdownSemError& x) {
+          // expected
+          EXPECT_TRUE(a.isShutdown());
+        }
+      }));
+    }
+    a.shutdown();
+    for (auto& thr : threads) {
+      DSched::join(thr);
+    }
+  }
+}
+
+TEST(LifoSem, multi_try_wait_simple) {
+  LifoSem sem;
+  sem.post(5);
+  auto n = sem.tryWait(10);     // this used to trigger an assert
+  ASSERT_EQ(5, n);
+}
+
+TEST(LifoSem, multi_try_wait) {
+  long seed = folly::randomNumberSeed() % 10000;
+  LOG(INFO) << "seed=" << seed;
+  DSched sched(DSched::uniform(seed));
+  DLifoSem sem;
+
+  const int NPOSTS = 1000;
+
+  auto producer = [&]{
+    for (int i=0; i<NPOSTS; ++i) {
+      sem.post();
+    }
+  };
+
+  DeterministicAtomic<bool> consumer_stop(false);
+  int consumed = 0;
+
+  auto consumer = [&]{
+    bool stop;
+    do {
+      stop = consumer_stop.load();
+      int n;
+      do {
+        n = sem.tryWait(10);
+        consumed += n;
+      } while (n > 0);
+    } while (!stop);
+  };
+
+  std::thread producer_thread(DSched::thread(producer));
+  std::thread consumer_thread(DSched::thread(consumer));
+  DSched::join(producer_thread);
+  consumer_stop.store(true);
+  DSched::join(consumer_thread);
+
+  ASSERT_EQ(NPOSTS, consumed);
+}
+
+BENCHMARK(lifo_sem_pingpong, iters) {
+  LifoSem a;
+  LifoSem b;
+  auto thr = std::thread([&]{
+    for (size_t i = 0; i < iters; ++i) {
+      a.wait();
+      b.post();
+    }
+  });
+  for (size_t i = 0; i < iters; ++i) {
+    a.post();
+    b.wait();
+  }
+  thr.join();
+}
+
+BENCHMARK(lifo_sem_oneway, iters) {
+  LifoSem a;
+  auto thr = std::thread([&]{
+    for (size_t i = 0; i < iters; ++i) {
+      a.wait();
+    }
+  });
+  for (size_t i = 0; i < iters; ++i) {
+    a.post();
+  }
+  thr.join();
+}
+
+BENCHMARK(single_thread_lifo_post, iters) {
+  LifoSem sem;
+  for (size_t n = 0; n < iters; ++n) {
+    sem.post();
+    asm volatile ("":::"memory");
+  }
+}
+
+BENCHMARK(single_thread_lifo_wait, iters) {
+  LifoSem sem(iters);
+  for (size_t n = 0; n < iters; ++n) {
+    sem.wait();
+    asm volatile ("":::"memory");
+  }
+}
+
+BENCHMARK(single_thread_lifo_postwait, iters) {
+  LifoSem sem;
+  for (size_t n = 0; n < iters; ++n) {
+    sem.post();
+    asm volatile ("":::"memory");
+    sem.wait();
+    asm volatile ("":::"memory");
+  }
+}
+
+BENCHMARK(single_thread_lifo_trywait, iters) {
+  LifoSem sem;
+  for (size_t n = 0; n < iters; ++n) {
+    EXPECT_FALSE(sem.tryWait());
+    asm volatile ("":::"memory");
+  }
+}
+
+BENCHMARK(single_thread_posix_postwait, iters) {
+  sem_t sem;
+  EXPECT_EQ(sem_init(&sem, 0, 0), 0);
+  for (size_t n = 0; n < iters; ++n) {
+    EXPECT_EQ(sem_post(&sem), 0);
+    EXPECT_EQ(sem_wait(&sem), 0);
+  }
+  EXPECT_EQ(sem_destroy(&sem), 0);
+}
+
+BENCHMARK(single_thread_posix_trywait, iters) {
+  sem_t sem;
+  EXPECT_EQ(sem_init(&sem, 0, 0), 0);
+  for (size_t n = 0; n < iters; ++n) {
+    EXPECT_EQ(sem_trywait(&sem), -1);
+  }
+  EXPECT_EQ(sem_destroy(&sem), 0);
+}
+
+static void contendedUse(uint n, int posters, int waiters) {
+  LifoSemImpl<std::atomic> sem;
+
+  std::vector<std::thread> threads;
+  std::atomic<bool> go(false);
+
+  BENCHMARK_SUSPEND {
+    for (int t = 0; t < waiters; ++t) {
+      threads.emplace_back([=,&sem] {
+        for (uint i = t; i < n; i += waiters) {
+          sem.wait();
+        }
+      });
+    }
+    for (int t = 0; t < posters; ++t) {
+      threads.emplace_back([=,&sem,&go] {
+        while (!go.load()) {
+          std::this_thread::yield();
+        }
+        for (uint i = t; i < n; i += posters) {
+          sem.post();
+        }
+      });
+    }
+  }
+
+  go.store(true);
+  for (auto& thr : threads) {
+    thr.join();
+  }
+}
+
+BENCHMARK_DRAW_LINE()
+BENCHMARK_NAMED_PARAM(contendedUse, 1_to_1, 1, 1)
+BENCHMARK_NAMED_PARAM(contendedUse, 1_to_32, 1, 32)
+BENCHMARK_NAMED_PARAM(contendedUse, 32_to_1, 31, 1)
+BENCHMARK_NAMED_PARAM(contendedUse, 16_to_16, 16, 16)
+BENCHMARK_NAMED_PARAM(contendedUse, 32_to_32, 32, 32)
+BENCHMARK_NAMED_PARAM(contendedUse, 32_to_1000, 32, 1000)
+
+// sudo nice -n -20 folly/test/LifoSemTests --benchmark --bm_min_iters=10000000
+// ============================================================================
+// folly/test/LifoSemTests.cpp                     relative  time/iter  iters/s
+// ============================================================================
+// lifo_sem_pingpong                                          396.84ns    2.52M
+// lifo_sem_oneway                                             88.52ns   11.30M
+// single_thread_lifo_post                                     14.78ns   67.67M
+// single_thread_lifo_wait                                     13.53ns   73.90M
+// single_thread_lifo_postwait                                 28.91ns   34.59M
+// single_thread_lifo_trywait                                 670.13ps    1.49G
+// single_thread_posix_postwait                                24.12ns   41.46M
+// single_thread_posix_trywait                                  6.76ns  147.88M
+// ----------------------------------------------------------------------------
+// contendedUse(1_to_1)                                       143.60ns    6.96M
+// contendedUse(1_to_32)                                      244.06ns    4.10M
+// contendedUse(32_to_1)                                      131.99ns    7.58M
+// contendedUse(16_to_16)                                     210.64ns    4.75M
+// contendedUse(32_to_32)                                     222.91ns    4.49M
+// contendedUse(32_to_1000)                                   453.39ns    2.21M
+// ============================================================================
+
+int main(int argc, char ** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  int rv = RUN_ALL_TESTS();
+  folly::runBenchmarksOnFlag();
+  return rv;
+}
diff --git a/faux-folly/folly/test/LoggingTest.cpp b/faux-folly/folly/test/LoggingTest.cpp
new file mode 100644
index 0000000..ba778ef
--- /dev/null
+++ b/faux-folly/folly/test/LoggingTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 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/Logging.h>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include <folly/Benchmark.h>
+#include <vector>
+
+TEST(LogEveryMs, basic) {
+  std::vector<std::chrono::steady_clock::time_point> hist;
+
+  while (hist.size() < 10) {
+    FB_LOG_EVERY_MS(INFO, 10)
+      << "test msg " << (hist.push_back(std::chrono::steady_clock::now()),
+                         hist.size());
+  }
+
+  bool atLeastOneIsGood = false;
+  for (size_t i = 0; i < hist.size() - 1; ++i) {
+    auto delta = hist[i + 1] - hist[i];
+    if (delta > std::chrono::milliseconds(5) &&
+        delta < std::chrono::milliseconds(15)) {
+      atLeastOneIsGood = true;
+    }
+  }
+  EXPECT_TRUE(atLeastOneIsGood);
+}
+
+TEST(LogEveryMs, zero) {
+  int count = 0;
+
+  for (int i = 0; i < 10; ++i) {
+    FB_LOG_EVERY_MS(INFO, 0)
+      << "test msg " << ++count;
+  }
+
+  EXPECT_EQ(10, count);
+}
+
+BENCHMARK(skip_overhead, iter) {
+  auto prev = FLAGS_minloglevel;
+  FLAGS_minloglevel = 2;
+
+  for (unsigned i = 0; i < iter; ++i) {
+    FB_LOG_EVERY_MS(INFO, 1000) << "every 1s";
+  }
+
+  FLAGS_minloglevel = prev;
+}
+
+BENCHMARK(dev_null_log_overhead, iter) {
+  auto prev = FLAGS_minloglevel;
+  FLAGS_minloglevel = 2;
+
+  for (unsigned i = 0; i < iter; ++i) {
+    FB_LOG_EVERY_MS(INFO, -1) << "every -1ms";
+  }
+
+  FLAGS_minloglevel = prev;
+}
+
+// ============================================================================
+// folly/test/LoggingTest.cpp                      relative  time/iter  iters/s
+// ============================================================================
+// skip_overhead                                               36.37ns   27.49M
+// dev_null_log_overhead                                        2.61us  382.57K
+// ============================================================================
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto rv = RUN_ALL_TESTS();
+  if (!rv && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return rv;
+}
diff --git a/faux-folly/folly/test/MPMCQueueTest.cpp b/faux-folly/folly/test/MPMCQueueTest.cpp
new file mode 100644
index 0000000..e606a24
--- /dev/null
+++ b/faux-folly/folly/test/MPMCQueueTest.cpp
@@ -0,0 +1,723 @@
+/*
+ * Copyright 2015 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/MPMCQueue.h>
+#include <folly/Format.h>
+#include <folly/Memory.h>
+#include <folly/test/DeterministicSchedule.h>
+
+#include <boost/intrusive_ptr.hpp>
+#include <memory>
+#include <functional>
+#include <thread>
+#include <utility>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(boost::intrusive_ptr);
+
+using namespace folly;
+using namespace detail;
+using namespace test;
+
+typedef DeterministicSchedule DSched;
+
+template <template<typename> class Atom>
+void run_mt_sequencer_thread(
+    int numThreads,
+    int numOps,
+    uint32_t init,
+    TurnSequencer<Atom>& seq,
+    Atom<uint32_t>& spinThreshold,
+    int& prev,
+    int i) {
+  for (int op = i; op < numOps; op += numThreads) {
+    seq.waitForTurn(init + op, spinThreshold, (op % 32) == 0);
+    EXPECT_EQ(prev, op - 1);
+    prev = op;
+    seq.completeTurn(init + op);
+  }
+}
+
+template <template<typename> class Atom>
+void run_mt_sequencer_test(int numThreads, int numOps, uint32_t init) {
+  TurnSequencer<Atom> seq(init);
+  Atom<uint32_t> spinThreshold(0);
+
+  int prev = -1;
+  std::vector<std::thread> threads(numThreads);
+  for (int i = 0; i < numThreads; ++i) {
+    threads[i] = DSched::thread(std::bind(run_mt_sequencer_thread<Atom>,
+          numThreads, numOps, init, std::ref(seq), std::ref(spinThreshold),
+          std::ref(prev), i));
+  }
+
+  for (auto& thr : threads) {
+    DSched::join(thr);
+  }
+
+  EXPECT_EQ(prev, numOps - 1);
+}
+
+TEST(MPMCQueue, sequencer) {
+  run_mt_sequencer_test<std::atomic>(1, 100, 0);
+  run_mt_sequencer_test<std::atomic>(2, 100000, -100);
+  run_mt_sequencer_test<std::atomic>(100, 10000, -100);
+}
+
+TEST(MPMCQueue, sequencer_emulated_futex) {
+  run_mt_sequencer_test<EmulatedFutexAtomic>(1, 100, 0);
+  run_mt_sequencer_test<EmulatedFutexAtomic>(2, 100000, -100);
+  run_mt_sequencer_test<EmulatedFutexAtomic>(100, 10000, -100);
+}
+
+TEST(MPMCQueue, sequencer_deterministic) {
+  DSched sched(DSched::uniform(0));
+  run_mt_sequencer_test<DeterministicAtomic>(1, 100, -50);
+  run_mt_sequencer_test<DeterministicAtomic>(2, 10000, (1 << 29) - 100);
+  run_mt_sequencer_test<DeterministicAtomic>(10, 1000, -100);
+}
+
+template <typename T>
+void runElementTypeTest(T&& src) {
+  MPMCQueue<T> cq(10);
+  cq.blockingWrite(std::move(src));
+  T dest;
+  cq.blockingRead(dest);
+  EXPECT_TRUE(cq.write(std::move(dest)));
+  EXPECT_TRUE(cq.read(dest));
+}
+
+struct RefCounted {
+  static __thread int active_instances;
+
+  mutable std::atomic<int> rc;
+
+  RefCounted() : rc(0) {
+    ++active_instances;
+  }
+
+  ~RefCounted() {
+    --active_instances;
+  }
+};
+__thread int RefCounted::active_instances;
+
+
+void intrusive_ptr_add_ref(RefCounted const* p) {
+  p->rc++;
+}
+
+void intrusive_ptr_release(RefCounted const* p) {
+  if (--(p->rc) == 0) {
+    delete p;
+  }
+}
+
+TEST(MPMCQueue, lots_of_element_types) {
+  runElementTypeTest(10);
+  runElementTypeTest(std::string("abc"));
+  runElementTypeTest(std::make_pair(10, std::string("def")));
+  runElementTypeTest(std::vector<std::string>{ { "abc" } });
+  runElementTypeTest(std::make_shared<char>('a'));
+  runElementTypeTest(folly::make_unique<char>('a'));
+  runElementTypeTest(boost::intrusive_ptr<RefCounted>(new RefCounted));
+  EXPECT_EQ(RefCounted::active_instances, 0);
+}
+
+TEST(MPMCQueue, single_thread_enqdeq) {
+  MPMCQueue<int> cq(10);
+
+  for (int pass = 0; pass < 10; ++pass) {
+    for (int i = 0; i < 10; ++i) {
+      EXPECT_TRUE(cq.write(i));
+    }
+    EXPECT_FALSE(cq.write(-1));
+    EXPECT_FALSE(cq.isEmpty());
+    EXPECT_EQ(cq.size(), 10);
+
+    for (int i = 0; i < 5; ++i) {
+      int dest = -1;
+      EXPECT_TRUE(cq.read(dest));
+      EXPECT_EQ(dest, i);
+    }
+    for (int i = 5; i < 10; ++i) {
+      int dest = -1;
+      cq.blockingRead(dest);
+      EXPECT_EQ(dest, i);
+    }
+    int dest = -1;
+    EXPECT_FALSE(cq.read(dest));
+    EXPECT_EQ(dest, -1);
+
+    EXPECT_TRUE(cq.isEmpty());
+    EXPECT_EQ(cq.size(), 0);
+  }
+}
+
+TEST(MPMCQueue, tryenq_capacity_test) {
+  for (size_t cap = 1; cap < 100; ++cap) {
+    MPMCQueue<int> cq(cap);
+    for (size_t i = 0; i < cap; ++i) {
+      EXPECT_TRUE(cq.write(i));
+    }
+    EXPECT_FALSE(cq.write(100));
+  }
+}
+
+TEST(MPMCQueue, enq_capacity_test) {
+  for (auto cap : { 1, 100, 10000 }) {
+    MPMCQueue<int> cq(cap);
+    for (int i = 0; i < cap; ++i) {
+      cq.blockingWrite(i);
+    }
+    int t = 0;
+    int when;
+    auto thr = std::thread([&]{
+      cq.blockingWrite(100);
+      when = t;
+    });
+    usleep(2000);
+    t = 1;
+    int dummy;
+    cq.blockingRead(dummy);
+    thr.join();
+    EXPECT_EQ(when, 1);
+  }
+}
+
+template <template<typename> class Atom>
+void runTryEnqDeqThread(
+    int numThreads,
+    int n, /*numOps*/
+    MPMCQueue<int, Atom>& cq,
+    std::atomic<uint64_t>& sum,
+    int t) {
+  uint64_t threadSum = 0;
+  int src = t;
+  // received doesn't reflect any actual values, we just start with
+  // t and increment by numThreads to get the rounding of termination
+  // correct if numThreads doesn't evenly divide numOps
+  int received = t;
+  while (src < n || received < n) {
+    if (src < n && cq.write(src)) {
+      src += numThreads;
+    }
+
+    int dst;
+    if (received < n && cq.read(dst)) {
+      received += numThreads;
+      threadSum += dst;
+    }
+  }
+  sum += threadSum;
+}
+
+template <template<typename> class Atom>
+void runTryEnqDeqTest(int numThreads, int numOps) {
+  // write and read aren't linearizable, so we don't have
+  // hard guarantees on their individual behavior.  We can still test
+  // correctness in aggregate
+  MPMCQueue<int,Atom> cq(numThreads);
+
+  uint64_t n = numOps;
+  std::vector<std::thread> threads(numThreads);
+  std::atomic<uint64_t> sum(0);
+  for (int t = 0; t < numThreads; ++t) {
+    threads[t] = DSched::thread(std::bind(runTryEnqDeqThread<Atom>,
+          numThreads, n, std::ref(cq), std::ref(sum), t));
+  }
+  for (auto& t : threads) {
+    DSched::join(t);
+  }
+  EXPECT_TRUE(cq.isEmpty());
+  EXPECT_EQ(n * (n - 1) / 2 - sum, 0);
+}
+
+TEST(MPMCQueue, mt_try_enq_deq) {
+  int nts[] = { 1, 3, 100 };
+
+  int n = 100000;
+  for (int nt : nts) {
+    runTryEnqDeqTest<std::atomic>(nt, n);
+  }
+}
+
+TEST(MPMCQueue, mt_try_enq_deq_emulated_futex) {
+  int nts[] = { 1, 3, 100 };
+
+  int n = 100000;
+  for (int nt : nts) {
+    runTryEnqDeqTest<EmulatedFutexAtomic>(nt, n);
+  }
+}
+
+TEST(MPMCQueue, mt_try_enq_deq_deterministic) {
+  int nts[] = { 3, 10 };
+
+  long seed = 0;
+  LOG(INFO) << "using seed " << seed;
+
+  int n = 1000;
+  for (int nt : nts) {
+    {
+      DSched sched(DSched::uniform(seed));
+      runTryEnqDeqTest<DeterministicAtomic>(nt, n);
+    }
+    {
+      DSched sched(DSched::uniformSubset(seed, 2));
+      runTryEnqDeqTest<DeterministicAtomic>(nt, n);
+    }
+  }
+}
+
+uint64_t nowMicro() {
+  timeval tv;
+  gettimeofday(&tv, 0);
+  return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+}
+
+template <typename Q>
+std::string producerConsumerBench(Q&& queue, std::string qName,
+                                  int numProducers, int numConsumers,
+                                  int numOps, bool ignoreContents = false) {
+  Q& q = queue;
+
+  struct rusage beginUsage;
+  getrusage(RUSAGE_SELF, &beginUsage);
+
+  auto beginMicro = nowMicro();
+
+  uint64_t n = numOps;
+  std::atomic<uint64_t> sum(0);
+
+  std::vector<std::thread> producers(numProducers);
+  for (int t = 0; t < numProducers; ++t) {
+    producers[t] = DSched::thread([&,t]{
+      for (int i = t; i < numOps; i += numProducers) {
+        q.blockingWrite(i);
+      }
+    });
+  }
+
+  std::vector<std::thread> consumers(numConsumers);
+  for (int t = 0; t < numConsumers; ++t) {
+    consumers[t] = DSched::thread([&,t]{
+      uint64_t localSum = 0;
+      for (int i = t; i < numOps; i += numConsumers) {
+        int dest = -1;
+        q.blockingRead(dest);
+        EXPECT_FALSE(dest == -1);
+        localSum += dest;
+      }
+      sum += localSum;
+    });
+  }
+
+  for (auto& t : producers) {
+    DSched::join(t);
+  }
+  for (auto& t : consumers) {
+    DSched::join(t);
+  }
+  if (!ignoreContents) {
+    EXPECT_EQ(n * (n - 1) / 2 - sum, 0);
+  }
+
+  auto endMicro = nowMicro();
+
+  struct rusage endUsage;
+  getrusage(RUSAGE_SELF, &endUsage);
+
+  uint64_t nanosPer = (1000 * (endMicro - beginMicro)) / n;
+  long csw = endUsage.ru_nvcsw + endUsage.ru_nivcsw -
+      (beginUsage.ru_nvcsw + beginUsage.ru_nivcsw);
+
+  return folly::format(
+      "{}, {} producers, {} consumers => {} nanos/handoff, {} csw / {} handoff",
+      qName, numProducers, numConsumers, nanosPer, csw, n).str();
+}
+
+
+TEST(MPMCQueue, mt_prod_cons_deterministic) {
+  // we use the Bench method, but perf results are meaningless under DSched
+  DSched sched(DSched::uniform(0));
+
+  producerConsumerBench(MPMCQueue<int,DeterministicAtomic>(10),
+          "", 1, 1, 1000);
+  producerConsumerBench(MPMCQueue<int,DeterministicAtomic>(100),
+          "", 10, 10, 1000);
+  producerConsumerBench(MPMCQueue<int,DeterministicAtomic>(10),
+          "", 1, 1, 1000);
+  producerConsumerBench(MPMCQueue<int,DeterministicAtomic>(100),
+          "", 10, 10, 1000);
+  producerConsumerBench(MPMCQueue<int,DeterministicAtomic>(1),
+          "", 10, 10, 1000);
+}
+
+#define PC_BENCH(q, np, nc, ...) \
+    producerConsumerBench(q, #q, (np), (nc), __VA_ARGS__)
+
+TEST(MPMCQueue, mt_prod_cons) {
+  int n = 100000;
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10), 1, 1, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10), 10, 1, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10), 1, 10, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10), 10, 10, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10000), 1, 1, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10000), 10, 1, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10000), 1, 10, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(10000), 10, 10, n);
+  LOG(INFO) << PC_BENCH(MPMCQueue<int>(100000), 32, 100, n);
+}
+
+TEST(MPMCQueue, mt_prod_cons_emulated_futex) {
+  int n = 100000;
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10)), 1, 1, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10)), 10, 1, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10)), 1, 10, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10)), 10, 10, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10000)), 1, 1, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10000)), 10, 1, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10000)), 1, 10, n);
+  LOG(INFO) << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(10000)), 10, 10, n);
+  LOG(INFO)
+    << PC_BENCH((MPMCQueue<int,EmulatedFutexAtomic>(100000)), 32, 100, n);
+}
+
+template <template<typename> class Atom>
+void runNeverFailThread(
+    int numThreads,
+    int n, /*numOps*/
+    MPMCQueue<int, Atom>& cq,
+    std::atomic<uint64_t>& sum,
+    int t) {
+  uint64_t threadSum = 0;
+  for (int i = t; i < n; i += numThreads) {
+    // enq + deq
+    EXPECT_TRUE(cq.writeIfNotFull(i));
+
+    int dest = -1;
+    EXPECT_TRUE(cq.readIfNotEmpty(dest));
+    EXPECT_TRUE(dest >= 0);
+    threadSum += dest;
+  }
+  sum += threadSum;
+}
+
+template <template<typename> class Atom>
+uint64_t runNeverFailTest(int numThreads, int numOps) {
+  // always #enq >= #deq
+  MPMCQueue<int,Atom> cq(numThreads);
+
+  uint64_t n = numOps;
+  auto beginMicro = nowMicro();
+
+  std::vector<std::thread> threads(numThreads);
+  std::atomic<uint64_t> sum(0);
+  for (int t = 0; t < numThreads; ++t) {
+    threads[t] = DSched::thread(std::bind(runNeverFailThread<Atom>,
+          numThreads, n, std::ref(cq), std::ref(sum), t));
+  }
+  for (auto& t : threads) {
+    DSched::join(t);
+  }
+  EXPECT_TRUE(cq.isEmpty());
+  EXPECT_EQ(n * (n - 1) / 2 - sum, 0);
+
+  return nowMicro() - beginMicro;
+}
+
+TEST(MPMCQueue, mt_never_fail) {
+  int nts[] = { 1, 3, 100 };
+
+  int n = 100000;
+  for (int nt : nts) {
+    uint64_t elapsed = runNeverFailTest<std::atomic>(nt, n);
+    LOG(INFO) << (elapsed * 1000.0) / (n * 2) << " nanos per op with "
+              << nt << " threads";
+  }
+}
+
+TEST(MPMCQueue, mt_never_fail_emulated_futex) {
+  int nts[] = { 1, 3, 100 };
+
+  int n = 100000;
+  for (int nt : nts) {
+    uint64_t elapsed = runNeverFailTest<EmulatedFutexAtomic>(nt, n);
+    LOG(INFO) << (elapsed * 1000.0) / (n * 2) << " nanos per op with "
+              << nt << " threads";
+  }
+}
+
+TEST(MPMCQueue, mt_never_fail_deterministic) {
+  int nts[] = { 3, 10 };
+
+  long seed = 0; // nowMicro() % 10000;
+  LOG(INFO) << "using seed " << seed;
+
+  int n = 1000;
+  for (int nt : nts) {
+    {
+      DSched sched(DSched::uniform(seed));
+      runNeverFailTest<DeterministicAtomic>(nt, n);
+    }
+    {
+      DSched sched(DSched::uniformSubset(seed, 2));
+      runNeverFailTest<DeterministicAtomic>(nt, n);
+    }
+  }
+}
+
+enum LifecycleEvent {
+  NOTHING = -1,
+  DEFAULT_CONSTRUCTOR,
+  COPY_CONSTRUCTOR,
+  MOVE_CONSTRUCTOR,
+  TWO_ARG_CONSTRUCTOR,
+  COPY_OPERATOR,
+  MOVE_OPERATOR,
+  DESTRUCTOR,
+  MAX_LIFECYCLE_EVENT
+};
+
+static FOLLY_TLS int lc_counts[MAX_LIFECYCLE_EVENT];
+static FOLLY_TLS int lc_prev[MAX_LIFECYCLE_EVENT];
+
+static int lc_outstanding() {
+  return lc_counts[DEFAULT_CONSTRUCTOR] + lc_counts[COPY_CONSTRUCTOR] +
+      lc_counts[MOVE_CONSTRUCTOR] + lc_counts[TWO_ARG_CONSTRUCTOR] -
+      lc_counts[DESTRUCTOR];
+}
+
+static void lc_snap() {
+  for (int i = 0; i < MAX_LIFECYCLE_EVENT; ++i) {
+    lc_prev[i] = lc_counts[i];
+  }
+}
+
+#define LIFECYCLE_STEP(...) lc_step(__LINE__, __VA_ARGS__)
+
+static void lc_step(int lineno, int what = NOTHING, int what2 = NOTHING) {
+  for (int i = 0; i < MAX_LIFECYCLE_EVENT; ++i) {
+    int delta = i == what || i == what2 ? 1 : 0;
+    EXPECT_EQ(lc_counts[i] - lc_prev[i], delta)
+        << "lc_counts[" << i << "] - lc_prev[" << i << "] was "
+        << (lc_counts[i] - lc_prev[i]) << ", expected " << delta
+        << ", from line " << lineno;
+  }
+  lc_snap();
+}
+
+template <typename R>
+struct Lifecycle {
+  typedef R IsRelocatable;
+
+  bool constructed;
+
+  Lifecycle() noexcept : constructed(true) {
+    ++lc_counts[DEFAULT_CONSTRUCTOR];
+  }
+
+  explicit Lifecycle(int n, char const* s) noexcept : constructed(true) {
+    ++lc_counts[TWO_ARG_CONSTRUCTOR];
+  }
+
+  Lifecycle(const Lifecycle& rhs) noexcept : constructed(true) {
+    ++lc_counts[COPY_CONSTRUCTOR];
+  }
+
+  Lifecycle(Lifecycle&& rhs) noexcept : constructed(true) {
+    ++lc_counts[MOVE_CONSTRUCTOR];
+  }
+
+  Lifecycle& operator= (const Lifecycle& rhs) noexcept {
+    ++lc_counts[COPY_OPERATOR];
+    return *this;
+  }
+
+  Lifecycle& operator= (Lifecycle&& rhs) noexcept {
+    ++lc_counts[MOVE_OPERATOR];
+    return *this;
+  }
+
+  ~Lifecycle() noexcept {
+    ++lc_counts[DESTRUCTOR];
+    assert(lc_outstanding() >= 0);
+    assert(constructed);
+    constructed = false;
+  }
+};
+
+template <typename R>
+void runPerfectForwardingTest() {
+  lc_snap();
+  EXPECT_EQ(lc_outstanding(), 0);
+
+  {
+    MPMCQueue<Lifecycle<R>> queue(50);
+    LIFECYCLE_STEP(NOTHING);
+
+    for (int pass = 0; pass < 10; ++pass) {
+      for (int i = 0; i < 10; ++i) {
+        queue.blockingWrite();
+        LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+
+        queue.blockingWrite(1, "one");
+        LIFECYCLE_STEP(TWO_ARG_CONSTRUCTOR);
+
+        {
+          Lifecycle<R> src;
+          LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+          queue.blockingWrite(std::move(src));
+          LIFECYCLE_STEP(MOVE_CONSTRUCTOR);
+        }
+        LIFECYCLE_STEP(DESTRUCTOR);
+
+        {
+          Lifecycle<R> src;
+          LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+          queue.blockingWrite(src);
+          LIFECYCLE_STEP(COPY_CONSTRUCTOR);
+        }
+        LIFECYCLE_STEP(DESTRUCTOR);
+
+        EXPECT_TRUE(queue.write());
+        LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+      }
+
+      EXPECT_EQ(queue.size(), 50);
+      EXPECT_FALSE(queue.write(2, "two"));
+      LIFECYCLE_STEP(NOTHING);
+
+      for (int i = 0; i < 50; ++i) {
+        {
+          Lifecycle<R> node;
+          LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+
+          queue.blockingRead(node);
+          if (R::value) {
+            // relocatable, moved via memcpy
+            LIFECYCLE_STEP(DESTRUCTOR);
+          } else {
+            LIFECYCLE_STEP(DESTRUCTOR, MOVE_OPERATOR);
+          }
+        }
+        LIFECYCLE_STEP(DESTRUCTOR);
+      }
+
+      EXPECT_EQ(queue.size(), 0);
+    }
+
+    // put one element back before destruction
+    {
+      Lifecycle<R> src(3, "three");
+      LIFECYCLE_STEP(TWO_ARG_CONSTRUCTOR);
+      queue.write(std::move(src));
+      LIFECYCLE_STEP(MOVE_CONSTRUCTOR);
+    }
+    LIFECYCLE_STEP(DESTRUCTOR); // destroy src
+  }
+  LIFECYCLE_STEP(DESTRUCTOR); // destroy queue
+
+  EXPECT_EQ(lc_outstanding(), 0);
+}
+
+TEST(MPMCQueue, perfect_forwarding) {
+  runPerfectForwardingTest<std::false_type>();
+}
+
+TEST(MPMCQueue, perfect_forwarding_relocatable) {
+  runPerfectForwardingTest<std::true_type>();
+}
+
+TEST(MPMCQueue, queue_moving) {
+  lc_snap();
+  EXPECT_EQ(lc_outstanding(), 0);
+
+  {
+    MPMCQueue<Lifecycle<std::false_type>> a(50);
+    LIFECYCLE_STEP(NOTHING);
+
+    a.blockingWrite();
+    LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+
+    // move constructor
+    MPMCQueue<Lifecycle<std::false_type>> b = std::move(a);
+    LIFECYCLE_STEP(NOTHING);
+    EXPECT_EQ(a.capacity(), 0);
+    EXPECT_EQ(a.size(), 0);
+    EXPECT_EQ(b.capacity(), 50);
+    EXPECT_EQ(b.size(), 1);
+
+    b.blockingWrite();
+    LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+
+    // move operator
+    MPMCQueue<Lifecycle<std::false_type>> c;
+    LIFECYCLE_STEP(NOTHING);
+    c = std::move(b);
+    LIFECYCLE_STEP(NOTHING);
+    EXPECT_EQ(c.capacity(), 50);
+    EXPECT_EQ(c.size(), 2);
+
+    {
+      Lifecycle<std::false_type> dst;
+      LIFECYCLE_STEP(DEFAULT_CONSTRUCTOR);
+      c.blockingRead(dst);
+      LIFECYCLE_STEP(DESTRUCTOR, MOVE_OPERATOR);
+
+      {
+        // swap
+        MPMCQueue<Lifecycle<std::false_type>> d(10);
+        LIFECYCLE_STEP(NOTHING);
+        std::swap(c, d);
+        LIFECYCLE_STEP(NOTHING);
+        EXPECT_EQ(c.capacity(), 10);
+        EXPECT_TRUE(c.isEmpty());
+        EXPECT_EQ(d.capacity(), 50);
+        EXPECT_EQ(d.size(), 1);
+
+        d.blockingRead(dst);
+        LIFECYCLE_STEP(DESTRUCTOR, MOVE_OPERATOR);
+
+        c.blockingWrite(dst);
+        LIFECYCLE_STEP(COPY_CONSTRUCTOR);
+
+        d.blockingWrite(std::move(dst));
+        LIFECYCLE_STEP(MOVE_CONSTRUCTOR);
+      } // d goes out of scope
+      LIFECYCLE_STEP(DESTRUCTOR);
+    } // dst goes out of scope
+    LIFECYCLE_STEP(DESTRUCTOR);
+  } // c goes out of scope
+  LIFECYCLE_STEP(DESTRUCTOR);
+}
+
+TEST(MPMCQueue, explicit_zero_capacity_fail) {
+  ASSERT_THROW(MPMCQueue<int> cq(0), std::invalid_argument);
+}
+
+
+int main(int argc, char ** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/Makefile.am b/faux-folly/folly/test/Makefile.am
new file mode 100644
index 0000000..f521d5b
--- /dev/null
+++ b/faux-folly/folly/test/Makefile.am
@@ -0,0 +1,152 @@
+SUBDIRS = . function_benchmark
+
+ACLOCAL_AMFLAGS = -I m4
+
+CPPFLAGS += -Igtest-1.7.0/include
+
+TESTS= \
+	foreach_test \
+	hash_test \
+	conv_test \
+	range_test \
+	bits_test \
+	bit_iterator_test
+
+check_LTLIBRARIES = libgtestmain.la libgtest.la
+check_PROGRAMS =
+
+libgtestmain_la_CPPFLAGS = -Igtest-1.7.0 -Igtest-1.7.0/src
+libgtestmain_la_SOURCES = gtest-1.7.0/src/gtest-all.cc gtest-1.7.0/src/gtest_main.cc
+
+libgtest_la_CPPFLAGS = -Igtest-1.7.0 -Igtest-1.7.0/src
+libgtest_la_SOURCES = gtest-1.7.0/src/gtest-all.cc
+
+noinst_HEADERS = FBStringTestBenchmarks.cpp.h \
+		 FBVectorTestBenchmarks.cpp.h
+
+foreach_test_SOURCES = ForeachTest.cpp
+foreach_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+
+hash_test_SOURCES = HashTest.cpp
+hash_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+
+
+fbstring_test_using_jemalloc_SOURCES = FBStringTest.cpp
+fbstring_test_using_jemalloc_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+TESTS += fbstring_test_using_jemalloc
+
+thread_cached_int_test_SOURCES = ThreadCachedIntTest.cpp
+thread_cached_int_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+
+thread_local_test_SOURCES = ThreadLocalTest.cpp
+thread_local_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+
+TESTS += thread_cached_int_test thread_local_test
+
+fbvector_test_SOURCES = FBVectorTest.cpp
+fbvector_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+TESTS += fbvector_test
+
+# fails due to cout
+dynamic_test_SOURCES = DynamicTest.cpp
+dynamic_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la $(top_builddir)/libfolly.la
+TESTS += dynamic_test
+
+benchmark_test_SOURCES = BenchmarkTest.cpp
+benchmark_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+check_PROGRAMS += benchmark_test
+
+# fails due to destructor
+scope_guard_test_SOURCES = ScopeGuardTest.cpp
+scope_guard_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += scope_guard_test
+
+conv_test_SOURCES = ConvTest.cpp
+conv_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+
+range_test_SOURCES = RangeTest.cpp
+range_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+
+bits_test_SOURCES = BitsTest.cpp
+bits_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+
+bit_iterator_test_SOURCES = BitIteratorTest.cpp
+bit_iterator_test_LDADD = libgtestmain.la $(top_builddir)/libfollybenchmark.la $(top_builddir)/libfolly.la
+
+endian_test_SOURCES = EndianTest.cpp
+endian_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += endian_test
+
+histogram_test_SOURCES = HistogramTest.cpp
+histogram_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += histogram_test
+
+map_util_test_SOURCES = MapUtilTest.cpp
+map_util_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += map_util_test
+
+string_test_SOURCES = StringTest.cpp
+string_test_LDADD = libgtest.la $(top_builddir)/libfolly.la $(top_builddir)/libfollybenchmark.la
+TESTS += string_test
+
+format_test_SOURCES = FormatTest.cpp
+format_test_LDADD = libgtest.la $(top_builddir)/libfolly.la $(top_builddir)/libfollybenchmark.la
+TESTS += format_test
+
+portability_test_SOURCES = PortabilityTest.cpp
+portability_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += portability_test
+
+spooky_hash_v1_test_SOURCES = SpookyHashV1Test.cpp
+spooky_hash_v1_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la $(top_builddir)/libfollybenchmark.la
+TESTS += spooky_hash_v1_test
+
+spooky_hash_v2_test_SOURCES = SpookyHashV2Test.cpp
+spooky_hash_v2_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la $(top_builddir)/libfollybenchmark.la
+TESTS += spooky_hash_v2_test
+
+cancellation_test_SOURCES = CancellationTest.cpp
+cancellation_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += cancellation_test
+
+token_bucket_test_SOURCES = TokenBucketTest.cpp
+token_bucket_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la $(top_builddir)/libfollybenchmark.la
+TESTS += token_bucket_test
+
+futures_test_SOURCES = \
+    ../futures/test/CancellationTest.cpp \
+    ../futures/test/CollectTest.cpp \
+    ../futures/test/ThreadedExecutorTest.cpp \
+    ../futures/test/EnsureTest.cpp \
+    ../futures/test/ExecutorTest.cpp \
+    ../futures/test/FSMTest.cpp \
+    ../futures/test/FilterTest.cpp \
+    ../futures/test/FutureTest.cpp \
+    ../futures/test/HeaderCompileTest.cpp \
+    ../futures/test/InterruptTest.cpp \
+    ../futures/test/MapTest.cpp \
+    ../futures/test/PollTest.cpp \
+    ../futures/test/PromiseTest.cpp \
+    ../futures/test/ReduceTest.cpp \
+    ../futures/test/RetryingTest.cpp \
+    ../futures/test/SharedPromiseTest.cpp \
+    ../futures/test/ThenCompileTest.cpp \
+    ../futures/test/ThenTest.cpp \
+    ../futures/test/ThreadWheelTimekeeperTest.cpp \
+    ../futures/test/TimekeeperTest.cpp \
+    ../futures/test/TimerMapTest.cpp \
+    ../futures/test/TimesTest.cpp \
+    ../futures/test/TryTest.cpp \
+    ../futures/test/UnitTest.cpp \
+    ../futures/test/UnwrapTest.cpp \
+    ../futures/test/ViaTest.cpp \
+    ../futures/test/WaitTest.cpp \
+    ../futures/test/WillEqualTest.cpp \
+    ../futures/test/WindowTest.cpp \
+    ../futures/test/WhenTest.cpp \
+    ../futures/test/WhileDoTest.cpp
+
+futures_test_LDADD = libgtestmain.la $(top_builddir)/libfolly.la
+TESTS += futures_test
+
+check_PROGRAMS += $(TESTS)
diff --git a/faux-folly/folly/test/MapUtilTest.cpp b/faux-folly/folly/test/MapUtilTest.cpp
new file mode 100644
index 0000000..ec915ed
--- /dev/null
+++ b/faux-folly/folly/test/MapUtilTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 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/MapUtil.h>
+
+#include <map>
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+TEST(MapUtil, get_default) {
+  std::map<int, int> m;
+  m[1] = 2;
+  EXPECT_EQ(2, get_default(m, 1, 42));
+  EXPECT_EQ(42, get_default(m, 2, 42));
+  EXPECT_EQ(0, get_default(m, 3));
+}
+
+TEST(MapUtil, get_or_throw) {
+  std::map<int, int> m;
+  m[1] = 2;
+  EXPECT_EQ(2, get_or_throw(m, 1));
+  EXPECT_THROW(get_or_throw(m, 2), std::out_of_range);
+}
+
+TEST(MapUtil, get_or_throw_specified) {
+  std::map<int, int> m;
+  m[1] = 2;
+  EXPECT_EQ(2, get_or_throw<std::runtime_error>(m, 1));
+  EXPECT_THROW(get_or_throw<std::runtime_error>(m, 2), std::runtime_error);
+}
+
+TEST(MapUtil, get_optional) {
+  std::map<int, int> m;
+  m[1] = 2;
+  EXPECT_TRUE(get_optional(m, 1).hasValue());
+  EXPECT_EQ(2, get_optional(m, 1).value());
+  EXPECT_FALSE(get_optional(m, 2).hasValue());
+}
+
+TEST(MapUtil, get_ref_default) {
+  std::map<int, int> m;
+  m[1] = 2;
+  const int i = 42;
+  EXPECT_EQ(2, get_ref_default(m, 1, i));
+  EXPECT_EQ(42, get_ref_default(m, 2, i));
+}
+
+TEST(MapUtil, get_ptr) {
+  std::map<int, int> m;
+  m[1] = 2;
+  EXPECT_EQ(2, *get_ptr(m, 1));
+  EXPECT_TRUE(get_ptr(m, 2) == nullptr);
+  *get_ptr(m, 1) = 4;
+  EXPECT_EQ(4, m.at(1));
+}
diff --git a/faux-folly/folly/test/MemoryIdlerTest.cpp b/faux-folly/folly/test/MemoryIdlerTest.cpp
new file mode 100644
index 0000000..c94bb43
--- /dev/null
+++ b/faux-folly/folly/test/MemoryIdlerTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2015 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/detail/MemoryIdler.h>
+#include <folly/Baton.h>
+#include <memory>
+#include <thread>
+#include <assert.h>
+#include <semaphore.h>
+#include <gflags/gflags.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <folly/Benchmark.h>
+
+using namespace folly;
+using namespace folly::detail;
+using namespace testing;
+
+TEST(MemoryIdler, releaseStack) {
+  MemoryIdler::unmapUnusedStack();
+}
+
+TEST(MemoryIdler, releaseStackMinExtra) {
+  MemoryIdler::unmapUnusedStack(0);
+}
+
+TEST(MemoryIdler, releaseStackLargeExtra) {
+  MemoryIdler::unmapUnusedStack(30000000);
+}
+
+TEST(MemoryIdler, releaseMallocTLS) {
+  auto p = new int[4];
+  MemoryIdler::flushLocalMallocCaches();
+  delete[] p;
+  MemoryIdler::flushLocalMallocCaches();
+  p = new int[4];
+  MemoryIdler::flushLocalMallocCaches();
+  delete[] p;
+}
+
+
+/// MockedAtom gives us a way to select a mocked Futex implementation
+/// inside Baton, even though the atom itself isn't exercised by the
+/// mocked futex
+template <typename T>
+struct MockAtom : public std::atomic<T> {
+  explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
+};
+
+
+/// MockClock is a bit tricky because we are mocking a static function
+/// (now()), so we need to find the corresponding mock instance without
+/// extending its scope beyond that of the test.  I generally avoid
+/// shared_ptr, but a weak_ptr is just the ticket here
+struct MockClock {
+  typedef std::chrono::steady_clock::duration duration;
+  typedef std::chrono::steady_clock::time_point time_point;
+
+  MOCK_METHOD0(nowImpl, time_point(void));
+
+  /// Hold on to the returned shared_ptr until the end of the test
+  static std::shared_ptr<StrictMock<MockClock>> setup() {
+    auto rv = std::make_shared<StrictMock<MockClock>>();
+    s_mockClockInstance = rv;
+    return rv;
+  }
+
+  static time_point now() {
+    return s_mockClockInstance.lock()->nowImpl();
+  }
+
+  static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
+};
+
+std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
+
+
+
+namespace folly { namespace detail {
+
+/// Futex<MockAtom> is our mocked futex implementation.  Note that the
+/// method signatures differ from the real Futex because we have elided
+/// unused default params and collapsed templated methods into the
+/// used type
+template<>
+struct Futex<MockAtom> {
+  MOCK_METHOD2(futexWait, bool(uint32_t, uint32_t));
+  MOCK_METHOD3(futexWaitUntil,
+               FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
+};
+
+}}
+
+TEST(MemoryIdler, futexWaitValueChangedEarly) {
+  StrictMock<Futex<MockAtom>> fut;
+  auto clock = MockClock::setup();
+  auto begin = MockClock::time_point(std::chrono::seconds(100));
+  auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
+
+  EXPECT_CALL(*clock, nowImpl())
+      .WillOnce(Return(begin));
+  EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
+                                           Lt(begin + 2 * idleTimeout)), -1))
+      .WillOnce(Return(FutexResult::VALUE_CHANGED));
+  EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
+}
+
+TEST(MemoryIdler, futexWaitValueChangedLate) {
+  StrictMock<Futex<MockAtom>> fut;
+  auto clock = MockClock::setup();
+  auto begin = MockClock::time_point(std::chrono::seconds(100));
+  auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
+
+  EXPECT_CALL(*clock, nowImpl())
+      .WillOnce(Return(begin));
+  EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
+                                           Lt(begin + 2 * idleTimeout)), -1))
+      .WillOnce(Return(FutexResult::TIMEDOUT));
+  EXPECT_CALL(fut, futexWait(1, -1))
+      .WillOnce(Return(false));
+  EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
+}
+
+TEST(MemoryIdler, futexWaitAwokenEarly) {
+  StrictMock<Futex<MockAtom>> fut;
+  auto clock = MockClock::setup();
+  auto begin = MockClock::time_point(std::chrono::seconds(100));
+  auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
+
+  EXPECT_CALL(*clock, nowImpl())
+      .WillOnce(Return(begin));
+  EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
+      .WillOnce(Return(FutexResult::AWOKEN));
+  EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
+}
+
+TEST(MemoryIdler, futexWaitAwokenLate) {
+  StrictMock<Futex<MockAtom>> fut;
+  auto clock = MockClock::setup();
+  auto begin = MockClock::time_point(std::chrono::seconds(100));
+  auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
+
+  EXPECT_CALL(*clock, nowImpl())
+      .WillOnce(Return(begin));
+  EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
+      .WillOnce(Return(FutexResult::TIMEDOUT));
+  EXPECT_CALL(fut, futexWait(1, -1))
+      .WillOnce(Return(true));
+  EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
+      fut, 1, -1, idleTimeout, 100, 0.0f)));
+}
+
+TEST(MemoryIdler, futexWaitImmediateFlush) {
+  StrictMock<Futex<MockAtom>> fut;
+  auto clock = MockClock::setup();
+
+  EXPECT_CALL(fut, futexWait(2, 0xff))
+      .WillOnce(Return(true));
+  EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
+      fut, 2, 0xff, std::chrono::seconds(0))));
+}
+
+TEST(MemoryIdler, futexWaitNeverFlush) {
+  StrictMock<Futex<MockAtom>> fut;
+  auto clock = MockClock::setup();
+
+  EXPECT_CALL(fut, futexWait(1, -1))
+      .WillOnce(Return(true));
+  EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
+      fut, 1, -1, MockClock::duration::max())));
+}
+
+
+BENCHMARK(releaseStack, iters) {
+  for (size_t i = 0; i < iters; ++i) {
+    MemoryIdler::unmapUnusedStack();
+  }
+}
+
+BENCHMARK(releaseMallocTLS, iters) {
+  for (size_t i = 0; i < iters; ++i) {
+    MemoryIdler::flushLocalMallocCaches();
+  }
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto rv = RUN_ALL_TESTS();
+  if (!rv && FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return rv;
+}
diff --git a/faux-folly/folly/test/MemoryMappingTest.cpp b/faux-folly/folly/test/MemoryMappingTest.cpp
new file mode 100644
index 0000000..70ecc71
--- /dev/null
+++ b/faux-folly/folly/test/MemoryMappingTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2015 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 <sys/mman.h>
+#include <cstdlib>
+#include <folly/FileUtil.h>
+#include <folly/MemoryMapping.h>
+#include <gtest/gtest.h>
+
+namespace folly {
+
+TEST(MemoryMapping, Basic) {
+  File f = File::temporary();
+  {
+    MemoryMapping m(File(f.fd()), 0, sizeof(double), MemoryMapping::writable());
+    double* d = m.asWritableRange<double>().data();
+    *d = 37 * M_PI;
+  }
+  {
+    MemoryMapping m(File(f.fd()), 0, 3);
+    EXPECT_EQ(0, m.asRange<int>().size()); // not big enough
+  }
+  {
+    MemoryMapping m(File(f.fd()), 0, sizeof(double));
+    const double* d = m.asRange<double>().data();
+    EXPECT_EQ(*d, 37 * M_PI);
+  }
+}
+
+TEST(MemoryMapping, Move) {
+  File f = File::temporary();
+  {
+    MemoryMapping m(
+        File(f.fd()), 0, sizeof(double) * 2, MemoryMapping::writable());
+    double* d = m.asWritableRange<double>().data();
+    d[0] = 37 * M_PI;
+    MemoryMapping m2(std::move(m));
+    double* d2 = m2.asWritableRange<double>().data();
+    d2[1] = 39 * M_PI;
+  }
+  {
+    MemoryMapping m(File(f.fd()), 0, sizeof(double));
+    const double* d = m.asRange<double>().data();
+    EXPECT_EQ(d[0], 37 * M_PI);
+    MemoryMapping m2(std::move(m));
+    const double* d2 = m2.asRange<double>().data();
+    EXPECT_EQ(d2[1], 39 * M_PI);
+  }
+}
+
+TEST(MemoryMapping, DoublyMapped) {
+  File f = File::temporary();
+  // two mappings of the same memory, different addresses.
+  MemoryMapping mw(File(f.fd()), 0, sizeof(double), MemoryMapping::writable());
+  MemoryMapping mr(File(f.fd()), 0, sizeof(double));
+
+  double* dw = mw.asWritableRange<double>().data();
+  const double* dr = mr.asRange<double>().data();
+
+  // Show that it's truly the same value, even though the pointers differ
+  EXPECT_NE(dw, dr);
+  *dw = 42 * M_PI;
+  EXPECT_EQ(*dr, 42 * M_PI);
+  *dw = 43 * M_PI;
+  EXPECT_EQ(*dr, 43 * M_PI);
+}
+
+namespace {
+
+void writeStringToFileOrDie(const std::string& str, int fd) {
+  const char* b = str.c_str();
+  size_t count = str.size();
+  ssize_t total_bytes = 0;
+  ssize_t r;
+  do {
+    r = write(fd, b, count);
+    if (r == -1) {
+      if (errno == EINTR) {
+        continue;
+      }
+      PCHECK(r) << "write";
+    }
+
+    total_bytes += r;
+    b += r;
+    count -= r;
+  } while (r != 0 && count);
+}
+
+}  // anonymous namespace
+
+TEST(MemoryMapping, Simple) {
+  File f = File::temporary();
+  writeStringToFileOrDie("hello", f.fd());
+
+  {
+    MemoryMapping m(File(f.fd()));
+    EXPECT_EQ("hello", m.data());
+  }
+  {
+    MemoryMapping m(File(f.fd()), 1, 2);
+    EXPECT_EQ("el", m.data());
+  }
+}
+
+TEST(MemoryMapping, LargeFile) {
+  std::string fileData;
+  size_t fileSize = sysconf(_SC_PAGESIZE) * 3 + 10;
+  fileData.reserve(fileSize);
+  for (size_t i = 0; i < fileSize; i++) {
+    fileData.push_back(0xff & random());
+  }
+
+  File f = File::temporary();
+  writeStringToFileOrDie(fileData, f.fd());
+
+  {
+    MemoryMapping m(File(f.fd()));
+    EXPECT_EQ(fileData, m.data());
+  }
+  {
+    size_t size = sysconf(_SC_PAGESIZE) * 2;
+    StringPiece s(fileData.data() + 9, size - 9);
+    MemoryMapping m(File(f.fd()), 9, size - 9);
+    EXPECT_EQ(s.toString(), m.data());
+  }
+}
+
+TEST(MemoryMapping, ZeroLength) {
+  File f = File::temporary();
+  MemoryMapping m(File(f.fd()));
+  EXPECT_TRUE(m.mlock(MemoryMapping::LockMode::MUST_LOCK));
+  EXPECT_TRUE(m.mlocked());
+  EXPECT_EQ(0, m.data().size());
+}
+
+TEST(MemoryMapping, Advise) {
+  File f = File::temporary();
+  size_t kPageSize = 4096;
+  size_t size = kPageSize + 10;  // unaligned file size
+  PCHECK(ftruncateNoInt(f.fd(), size) == 0) << size;
+
+  MemoryMapping m(File(f.fd()));
+
+  // NOTE: advise crashes on bad input.
+
+  m.advise(MADV_NORMAL, 0, kPageSize);
+  m.advise(MADV_NORMAL, 1, kPageSize);
+  m.advise(MADV_NORMAL, 0, 2);
+  m.advise(MADV_NORMAL, 1, 2);
+
+  m.advise(MADV_NORMAL, kPageSize, 0);
+  m.advise(MADV_NORMAL, kPageSize, 1);
+  m.advise(MADV_NORMAL, kPageSize, size - kPageSize);
+
+  auto off = kPageSize + 1;
+  m.advise(MADV_NORMAL, off, size - off);
+
+  EXPECT_DEATH(m.advise(MADV_NORMAL, off, size - off + 1), "");
+}
+
+} // namespace folly
diff --git a/faux-folly/folly/test/MergeTest.cpp b/faux-folly/folly/test/MergeTest.cpp
new file mode 100644
index 0000000..cc24301
--- /dev/null
+++ b/faux-folly/folly/test/MergeTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 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/Merge.h>
+#include <gtest/gtest.h>
+#include <map>
+#include <vector>
+
+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 (int 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/faux-folly/folly/test/MoveWrapperTest.cpp b/faux-folly/folly/test/MoveWrapperTest.cpp
new file mode 100644
index 0000000..b5a21c7
--- /dev/null
+++ b/faux-folly/folly/test/MoveWrapperTest.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/MoveWrapper.h>
+#include <memory>
+
+namespace folly {
+
+TEST(makeMoveWrapper, Empty) {
+  // checks for crashes
+  auto p = makeMoveWrapper(std::unique_ptr<int>());
+}
+
+TEST(makeMoveWrapper, NonEmpty) {
+  auto u = std::unique_ptr<int>(new int(5));
+  EXPECT_EQ(*u, 5);
+  auto p = makeMoveWrapper(std::move(u));
+  EXPECT_TRUE(!u);
+  EXPECT_EQ(**p, 5);
+}
+
+TEST(makeMoveWrapper, rvalue) {
+  std::unique_ptr<int> p;
+  makeMoveWrapper(std::move(p));
+}
+
+TEST(makeMoveWrapper, lvalue) {
+  std::unique_ptr<int> p;
+  makeMoveWrapper(p);
+}
+
+TEST(makeMoveWrapper, lvalue_copyable) {
+  std::shared_ptr<int> p;
+  makeMoveWrapper(p);
+}
+
+} // namespace
diff --git a/faux-folly/folly/test/OptionalTest.cpp b/faux-folly/folly/test/OptionalTest.cpp
new file mode 100644
index 0000000..974c8f4
--- /dev/null
+++ b/faux-folly/folly/test/OptionalTest.cpp
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2015 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/Optional.h>
+
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <iomanip>
+#include <string>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include <boost/optional.hpp>
+
+using std::unique_ptr;
+using std::shared_ptr;
+
+namespace folly {
+
+template<class V>
+std::ostream& operator<<(std::ostream& os, const Optional<V>& v) {
+  if (v) {
+    os << "Optional(" << v.value() << ')';
+  } else {
+    os << "None";
+  }
+  return os;
+}
+
+struct NoDefault {
+  NoDefault(int, int) {}
+  char a, b, c;
+};
+
+static_assert(sizeof(Optional<char>) == 2, "");
+static_assert(sizeof(Optional<int>) == 8, "");
+static_assert(sizeof(Optional<NoDefault>) == 4, "");
+static_assert(sizeof(Optional<char>) == sizeof(boost::optional<char>), "");
+static_assert(sizeof(Optional<short>) == sizeof(boost::optional<short>), "");
+static_assert(sizeof(Optional<int>) == sizeof(boost::optional<int>), "");
+static_assert(sizeof(Optional<double>) == sizeof(boost::optional<double>), "");
+
+TEST(Optional, NoDefault) {
+  Optional<NoDefault> x;
+  EXPECT_FALSE(x);
+  x.emplace(4, 5);
+  EXPECT_TRUE(bool(x));
+  x.clear();
+  EXPECT_FALSE(x);
+}
+
+TEST(Optional, String) {
+  Optional<std::string> maybeString;
+  EXPECT_FALSE(maybeString);
+  maybeString = "hello";
+  EXPECT_TRUE(bool(maybeString));
+}
+
+TEST(Optional, Const) {
+  { // default construct
+    Optional<const int> opt;
+    EXPECT_FALSE(bool(opt));
+    opt.emplace(4);
+    EXPECT_EQ(*opt, 4);
+    opt.emplace(5);
+    EXPECT_EQ(*opt, 5);
+    opt.clear();
+    EXPECT_FALSE(bool(opt));
+  }
+  { // copy-constructed
+    const int x = 6;
+    Optional<const int> opt(x);
+    EXPECT_EQ(*opt, 6);
+  }
+  { // move-constructed
+    const int x = 7;
+    Optional<const int> opt(std::move(x));
+    EXPECT_EQ(*opt, 7);
+  }
+  // no assignment allowed
+}
+
+TEST(Optional, Simple) {
+  Optional<int> opt;
+  EXPECT_FALSE(bool(opt));
+  EXPECT_EQ(42, opt.value_or(42));
+  opt = 4;
+  EXPECT_TRUE(bool(opt));
+  EXPECT_EQ(4, *opt);
+  EXPECT_EQ(4, opt.value_or(42));
+  opt = 5;
+  EXPECT_EQ(5, *opt);
+  opt.clear();
+  EXPECT_FALSE(bool(opt));
+}
+
+class MoveTester {
+public:
+  /* implicit */ MoveTester(const char* s) : s_(s) {}
+  MoveTester(const MoveTester&) = default;
+  MoveTester(MoveTester&& other) noexcept {
+    s_ = std::move(other.s_);
+    other.s_ = "";
+  }
+  MoveTester& operator=(const MoveTester&) = default;
+  MoveTester& operator=(MoveTester&&) = default;
+private:
+  friend bool operator==(const MoveTester& o1, const MoveTester& o2);
+  std::string s_;
+};
+
+bool operator==(const MoveTester& o1, const MoveTester& o2) {
+  return o1.s_ == o2.s_;
+}
+
+TEST(Optional, value_or_rvalue_arg) {
+  Optional<MoveTester> opt;
+  MoveTester dflt = "hello";
+  EXPECT_EQ("hello", opt.value_or(dflt));
+  EXPECT_EQ("hello", dflt);
+  EXPECT_EQ("hello", opt.value_or(std::move(dflt)));
+  EXPECT_EQ("", dflt);
+  EXPECT_EQ("world", opt.value_or("world"));
+
+  dflt = "hello";
+  // Make sure that the const overload works on const objects
+  const auto& optc = opt;
+  EXPECT_EQ("hello", optc.value_or(dflt));
+  EXPECT_EQ("hello", dflt);
+  EXPECT_EQ("hello", optc.value_or(std::move(dflt)));
+  EXPECT_EQ("", dflt);
+  EXPECT_EQ("world", optc.value_or("world"));
+
+  dflt = "hello";
+  opt = "meow";
+  EXPECT_EQ("meow", opt.value_or(dflt));
+  EXPECT_EQ("hello", dflt);
+  EXPECT_EQ("meow", opt.value_or(std::move(dflt)));
+  EXPECT_EQ("hello", dflt);  // only moved if used
+}
+
+TEST(Optional, value_or_noncopyable) {
+  Optional<std::unique_ptr<int>> opt;
+  std::unique_ptr<int> dflt(new int(42));
+  EXPECT_EQ(42, *std::move(opt).value_or(std::move(dflt)));
+}
+
+struct ExpectingDeleter {
+  explicit ExpectingDeleter(int expected) : expected(expected) { }
+  int expected;
+  void operator()(const int* ptr) {
+    EXPECT_EQ(*ptr, expected);
+    delete ptr;
+  }
+};
+
+TEST(Optional, value_life_extention) {
+  // Extends the life of the value.
+  const auto& ptr = Optional<std::unique_ptr<int, ExpectingDeleter>>(
+      {new int(42), ExpectingDeleter{1337}}).value();
+  *ptr = 1337;
+}
+
+TEST(Optional, value_move) {
+  auto ptr = Optional<std::unique_ptr<int, ExpectingDeleter>>(
+      {new int(42), ExpectingDeleter{1337}}).value();
+  *ptr = 1337;
+}
+
+TEST(Optional, dereference_life_extention) {
+  // Extends the life of the value.
+  const auto& ptr = *Optional<std::unique_ptr<int, ExpectingDeleter>>(
+      {new int(42), ExpectingDeleter{1337}});
+  *ptr = 1337;
+}
+
+TEST(Optional, dereference_move) {
+  auto ptr = *Optional<std::unique_ptr<int, ExpectingDeleter>>(
+      {new int(42), ExpectingDeleter{1337}});
+  *ptr = 1337;
+}
+
+TEST(Optional, EmptyConstruct) {
+  Optional<int> opt;
+  EXPECT_FALSE(bool(opt));
+  Optional<int> test1(opt);
+  EXPECT_FALSE(bool(test1));
+  Optional<int> test2(std::move(opt));
+  EXPECT_FALSE(bool(test2));
+}
+
+TEST(Optional, Unique) {
+  Optional<unique_ptr<int>> opt;
+
+  opt.clear();
+  EXPECT_FALSE(bool(opt));
+  // empty->emplaced
+  opt.emplace(new int(5));
+  EXPECT_TRUE(bool(opt));
+  EXPECT_EQ(5, **opt);
+
+  opt.clear();
+  // empty->moved
+  opt = unique_ptr<int>(new int(6));
+  EXPECT_EQ(6, **opt);
+  // full->moved
+  opt = unique_ptr<int>(new int(7));
+  EXPECT_EQ(7, **opt);
+
+  // move it out by move construct
+  Optional<unique_ptr<int>> moved(std::move(opt));
+  EXPECT_TRUE(bool(moved));
+  EXPECT_FALSE(bool(opt));
+  EXPECT_EQ(7, **moved);
+
+  EXPECT_TRUE(bool(moved));
+  opt = std::move(moved); // move it back by move assign
+  EXPECT_FALSE(bool(moved));
+  EXPECT_TRUE(bool(opt));
+  EXPECT_EQ(7, **opt);
+}
+
+TEST(Optional, Shared) {
+  shared_ptr<int> ptr;
+  Optional<shared_ptr<int>> opt;
+  EXPECT_FALSE(bool(opt));
+  // empty->emplaced
+  opt.emplace(new int(5));
+  EXPECT_TRUE(bool(opt));
+  ptr = opt.value();
+  EXPECT_EQ(ptr.get(), opt->get());
+  EXPECT_EQ(2, ptr.use_count());
+  opt.clear();
+  EXPECT_EQ(1, ptr.use_count());
+  // full->copied
+  opt = ptr;
+  EXPECT_EQ(2, ptr.use_count());
+  EXPECT_EQ(ptr.get(), opt->get());
+  opt.clear();
+  EXPECT_EQ(1, ptr.use_count());
+  // full->moved
+  opt = std::move(ptr);
+  EXPECT_EQ(1, opt->use_count());
+  EXPECT_EQ(nullptr, ptr.get());
+  {
+    Optional<shared_ptr<int>> copied(opt);
+    EXPECT_EQ(2, opt->use_count());
+    Optional<shared_ptr<int>> moved(std::move(opt));
+    EXPECT_EQ(2, moved->use_count());
+    moved.emplace(new int(6));
+    EXPECT_EQ(1, moved->use_count());
+    copied = moved;
+    EXPECT_EQ(2, moved->use_count());
+  }
+}
+
+TEST(Optional, Order) {
+  std::vector<Optional<int>> vect{
+    { none },
+    { 3 },
+    { 1 },
+    { none },
+    { 2 },
+  };
+  std::vector<Optional<int>> expected {
+    { none },
+    { none },
+    { 1 },
+    { 2 },
+    { 3 },
+  };
+  std::sort(vect.begin(), vect.end());
+  EXPECT_EQ(vect, expected);
+}
+
+TEST(Optional, Swap) {
+  Optional<std::string> a;
+  Optional<std::string> b;
+
+  swap(a, b);
+  EXPECT_FALSE(a.hasValue());
+  EXPECT_FALSE(b.hasValue());
+
+  a = "hello";
+  EXPECT_TRUE(a.hasValue());
+  EXPECT_FALSE(b.hasValue());
+  EXPECT_EQ("hello", a.value());
+
+  swap(a, b);
+  EXPECT_FALSE(a.hasValue());
+  EXPECT_TRUE(b.hasValue());
+  EXPECT_EQ("hello", b.value());
+
+  a = "bye";
+  EXPECT_TRUE(a.hasValue());
+  EXPECT_EQ("bye", a.value());
+
+  swap(a, b);
+}
+
+TEST(Optional, Comparisons) {
+  Optional<int> o_;
+  Optional<int> o1(1);
+  Optional<int> o2(2);
+
+  EXPECT_TRUE(o_ <= (o_));
+  EXPECT_TRUE(o_ == (o_));
+  EXPECT_TRUE(o_ >= (o_));
+
+  EXPECT_TRUE(o1 < o2);
+  EXPECT_TRUE(o1 <= o2);
+  EXPECT_TRUE(o1 <= (o1));
+  EXPECT_TRUE(o1 == (o1));
+  EXPECT_TRUE(o1 != o2);
+  EXPECT_TRUE(o1 >= (o1));
+  EXPECT_TRUE(o2 >= o1);
+  EXPECT_TRUE(o2 > o1);
+
+  EXPECT_FALSE(o2 < o1);
+  EXPECT_FALSE(o2 <= o1);
+  EXPECT_FALSE(o2 <= o1);
+  EXPECT_FALSE(o2 == o1);
+  EXPECT_FALSE(o1 != (o1));
+  EXPECT_FALSE(o1 >= o2);
+  EXPECT_FALSE(o1 >= o2);
+  EXPECT_FALSE(o1 > o2);
+
+  /* folly::Optional explicitly doesn't support comparisons with contained value
+  EXPECT_TRUE(1 < o2);
+  EXPECT_TRUE(1 <= o2);
+  EXPECT_TRUE(1 <= o1);
+  EXPECT_TRUE(1 == o1);
+  EXPECT_TRUE(2 != o1);
+  EXPECT_TRUE(1 >= o1);
+  EXPECT_TRUE(2 >= o1);
+  EXPECT_TRUE(2 > o1);
+
+  EXPECT_FALSE(o2 < 1);
+  EXPECT_FALSE(o2 <= 1);
+  EXPECT_FALSE(o2 <= 1);
+  EXPECT_FALSE(o2 == 1);
+  EXPECT_FALSE(o2 != 2);
+  EXPECT_FALSE(o1 >= 2);
+  EXPECT_FALSE(o1 >= 2);
+  EXPECT_FALSE(o1 > 2);
+  */
+
+  // boost::optional does support comparison with contained value, which can
+  // lead to confusion when a bool is contained
+  boost::optional<int> boi(3);
+  EXPECT_TRUE(boi < 5);
+  EXPECT_TRUE(boi <= 4);
+  EXPECT_TRUE(boi == 3);
+  EXPECT_TRUE(boi != 2);
+  EXPECT_TRUE(boi >= 1);
+  EXPECT_TRUE(boi > 0);
+  EXPECT_TRUE(1 <  boi);
+  EXPECT_TRUE(2 <= boi);
+  EXPECT_TRUE(3 == boi);
+  EXPECT_TRUE(4 != boi);
+  EXPECT_TRUE(5 >= boi);
+  EXPECT_TRUE(6 >  boi);
+
+  boost::optional<bool> bob(false);
+  EXPECT_TRUE((bool)bob);
+  EXPECT_TRUE(bob == false); // well that was confusing
+  EXPECT_FALSE(bob != false);
+}
+
+TEST(Optional, Conversions) {
+  Optional<bool> mbool;
+  Optional<short> mshort;
+  Optional<char*> mstr;
+  Optional<int> mint;
+
+  //These don't compile
+  //bool b = mbool;
+  //short s = mshort;
+  //char* c = mstr;
+  //int x = mint;
+  //char* c(mstr);
+  //short s(mshort);
+  //int x(mint);
+
+  // intended explicit operator bool, for if (opt).
+  bool b(mbool);
+
+  // Truthy tests work and are not ambiguous
+  if (mbool && mshort && mstr && mint) { // only checks not-empty
+    if (*mbool && *mshort && *mstr && *mint) { // only checks value
+      ;
+    }
+  }
+
+  mbool = false;
+  EXPECT_TRUE(bool(mbool));
+  EXPECT_FALSE(*mbool);
+
+  mbool = true;
+  EXPECT_TRUE(bool(mbool));
+  EXPECT_TRUE(*mbool);
+
+  mbool = none;
+  EXPECT_FALSE(bool(mbool));
+
+  // No conversion allowed; does not compile
+  // EXPECT_TRUE(mbool == false);
+}
+
+TEST(Optional, Pointee) {
+  Optional<int> x;
+  EXPECT_FALSE(get_pointer(x));
+  x = 1;
+  EXPECT_TRUE(get_pointer(x));
+  *get_pointer(x) = 2;
+  EXPECT_TRUE(*x == 2);
+  x = none;
+  EXPECT_FALSE(get_pointer(x));
+}
+
+TEST(Optional, MakeOptional) {
+  // const L-value version
+  const std::string s("abc");
+  auto optStr = make_optional(s);
+  ASSERT_TRUE(optStr.hasValue());
+  EXPECT_EQ(*optStr, "abc");
+  *optStr = "cde";
+  EXPECT_EQ(s, "abc");
+  EXPECT_EQ(*optStr, "cde");
+
+  // L-value version
+  std::string s2("abc");
+  auto optStr2 = make_optional(s2);
+  ASSERT_TRUE(optStr2.hasValue());
+  EXPECT_EQ(*optStr2, "abc");
+  *optStr2 = "cde";
+  // it's vital to check that s2 wasn't clobbered
+  EXPECT_EQ(s2, "abc");
+
+  // L-value reference version
+  std::string& s3(s2);
+  auto optStr3 = make_optional(s3);
+  ASSERT_TRUE(optStr3.hasValue());
+  EXPECT_EQ(*optStr3, "abc");
+  *optStr3 = "cde";
+  EXPECT_EQ(s3, "abc");
+
+  // R-value ref version
+  unique_ptr<int> pInt(new int(3));
+  auto optIntPtr = make_optional(std::move(pInt));
+  EXPECT_TRUE(pInt.get() == nullptr);
+  ASSERT_TRUE(optIntPtr.hasValue());
+  EXPECT_EQ(**optIntPtr, 3);
+}
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# if __clang_major__ > 3 || __clang_minor__ >= 6
+#  pragma clang diagnostic ignored "-Wself-move"
+# endif
+#endif
+
+TEST(Optional, SelfAssignment) {
+  Optional<int> a = 42;
+  a = a;
+  ASSERT_TRUE(a.hasValue() && a.value() == 42);
+
+  Optional<int> b = 23333333;
+  b = std::move(b);
+  ASSERT_TRUE(b.hasValue() && b.value() == 23333333);
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+class ContainsOptional {
+ public:
+  ContainsOptional() { }
+  explicit ContainsOptional(int x) : opt_(x) { }
+  bool hasValue() const { return opt_.hasValue(); }
+  int value() const { return opt_.value(); }
+
+  ContainsOptional(const ContainsOptional &other) = default;
+  ContainsOptional& operator=(const ContainsOptional &other) = default;
+  ContainsOptional(ContainsOptional &&other) = default;
+  ContainsOptional& operator=(ContainsOptional &&other) = default;
+
+ private:
+  Optional<int> opt_;
+};
+
+/**
+ * Test that a class containing an Optional can be copy and move assigned.
+ * This was broken under gcc 4.7 until assignment operators were explicitly
+ * defined.
+ */
+TEST(Optional, AssignmentContained) {
+  {
+    ContainsOptional source(5), target;
+    target = source;
+    EXPECT_TRUE(target.hasValue());
+    EXPECT_EQ(5, target.value());
+  }
+
+  {
+    ContainsOptional source(5), target;
+    target = std::move(source);
+    EXPECT_TRUE(target.hasValue());
+    EXPECT_EQ(5, target.value());
+    EXPECT_FALSE(source.hasValue());
+  }
+
+  {
+    ContainsOptional opt_uninit, target(10);
+    target = opt_uninit;
+    EXPECT_FALSE(target.hasValue());
+  }
+}
+
+TEST(Optional, Exceptions) {
+  Optional<int> empty;
+  EXPECT_THROW(empty.value(), OptionalEmptyException);
+}
+
+}
diff --git a/faux-folly/folly/test/PortabilityTest.cpp b/faux-folly/folly/test/PortabilityTest.cpp
new file mode 100644
index 0000000..62b2555
--- /dev/null
+++ b/faux-folly/folly/test/PortabilityTest.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 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/Portability.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+class Base {
+ public:
+  virtual ~Base() { }
+  virtual int foo() const { return 1; }
+};
+
+class Derived : public Base {
+ public:
+  int foo() const FOLLY_FINAL { return 2; }
+};
+
+// A compiler that supports final will likely inline the call to p->foo()
+// in fooDerived (but not in fooBase) as it knows that Derived::foo() can
+// no longer be overridden.
+int fooBase(const Base* p) { return p->foo() + 1; }
+int fooDerived(const Derived* p) { return p->foo() + 1; }
+
+TEST(Portability, Final) {
+  std::unique_ptr<Derived> p(new Derived);
+  EXPECT_EQ(3, fooBase(p.get()));
+  EXPECT_EQ(3, fooDerived(p.get()));
+}
diff --git a/faux-folly/folly/test/RandomTest.cpp b/faux-folly/folly/test/RandomTest.cpp
new file mode 100644
index 0000000..4a45dcc
--- /dev/null
+++ b/faux-folly/folly/test/RandomTest.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2015 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/Random.h>
+#include <folly/Range.h>
+#include <folly/Benchmark.h>
+#include <folly/Foreach.h>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <thread>
+#include <vector>
+#include <random>
+
+using namespace folly;
+
+TEST(Random, StateSize) {
+  using namespace folly::detail;
+
+  // uint_fast32_t is uint64_t on x86_64, w00t
+  EXPECT_EQ(sizeof(uint_fast32_t) / 4 + 3,
+            StateSize<std::minstd_rand0>::value);
+  EXPECT_EQ(624, StateSize<std::mt19937>::value);
+#if FOLLY_USE_SIMD_PRNG
+  EXPECT_EQ(624, StateSize<__gnu_cxx::sfmt19937>::value);
+#endif
+  EXPECT_EQ(24, StateSize<std::ranlux24_base>::value);
+}
+
+TEST(Random, Simple) {
+  uint32_t prev = 0, seed = 0;
+  for (int i = 0; i < 1024; ++i) {
+    EXPECT_NE(seed = randomNumberSeed(), prev);
+    prev = seed;
+  }
+}
+
+TEST(Random, MultiThreaded) {
+  const int n = 100;
+  std::vector<uint32_t> seeds(n);
+  std::vector<std::thread> threads;
+  for (int i = 0; i < n; ++i) {
+    threads.push_back(std::thread([i, &seeds] {
+      seeds[i] = randomNumberSeed();
+    }));
+  }
+  for (auto& t : threads) {
+    t.join();
+  }
+  std::sort(seeds.begin(), seeds.end());
+  for (int i = 0; i < n-1; ++i) {
+    EXPECT_LT(seeds[i], seeds[i+1]);
+  }
+}
+
+BENCHMARK(minstdrand, n) {
+  BenchmarkSuspender braces;
+  std::random_device rd;
+  std::minstd_rand rng(rd());
+
+  braces.dismiss();
+
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(rng());
+  }
+}
+
+BENCHMARK(mt19937, n) {
+  BenchmarkSuspender braces;
+  std::random_device rd;
+  std::mt19937 rng(rd());
+
+  braces.dismiss();
+
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(rng());
+  }
+}
+
+BENCHMARK(threadprng, n) {
+  BenchmarkSuspender braces;
+  ThreadLocalPRNG tprng;
+  tprng();
+
+  braces.dismiss();
+
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(tprng());
+  }
+}
+
+BENCHMARK(RandomDouble) { doNotOptimizeAway(Random::randDouble01()); }
+BENCHMARK(Random32) { doNotOptimizeAway(Random::rand32()); }
+BENCHMARK(Random32Num) { doNotOptimizeAway(Random::rand32(100)); }
+BENCHMARK(Random64) { doNotOptimizeAway(Random::rand64()); }
+BENCHMARK(Random64Num) { doNotOptimizeAway(Random::rand64(100ul << 32)); }
+BENCHMARK(Random64OneIn) { doNotOptimizeAway(Random::oneIn(100)); }
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/RangeFindBenchmark.cpp b/faux-folly/folly/test/RangeFindBenchmark.cpp
new file mode 100644
index 0000000..7ad15ae
--- /dev/null
+++ b/faux-folly/folly/test/RangeFindBenchmark.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2015 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/Range.h>
+#include <folly/Benchmark.h>
+#include <folly/Foreach.h>
+#include <algorithm>
+#include <iostream>
+#include <random>
+#include <string>
+
+namespace folly { namespace detail {
+// declaration of functions in Range.cpp
+size_t qfind_first_byte_of_byteset(const StringPiece haystack,
+                                   const StringPiece needles);
+
+size_t qfind_first_byte_of_nosse(const StringPiece haystack,
+                                 const StringPiece needles);
+}}
+
+using namespace folly;
+using namespace std;
+
+namespace {
+
+std::string str;
+constexpr int kVstrSize = 16;
+std::vector<std::string> vstr;
+std::vector<StringPiece> vstrp;
+std::string file;
+
+void initStr(int len) {
+  cout << "string length " << len << ':' << endl;
+  str.clear();
+  str.reserve(len + 1);
+  str.append(len, 'a');
+  str.append(1, 'b');
+
+  // create 16 copies of str, each with a different 16byte alignment.
+  // Useful because some implementations of find_first_of have different
+  // behaviors based on byte alignment.
+  for (int i = 0; i < kVstrSize; ++i) {
+    string s(i, '$');
+    s += str;
+    if (i % 2) {
+      // some find_first_of implementations have special (page-safe) logic
+      // for handling the end of a string.  Flex that logic only sometimes.
+      s += string(32, '$');
+    }
+    vstr.push_back(s);
+    StringPiece sp(vstr.back());
+    sp.advance(i);
+    vstrp.push_back(sp);
+  }
+}
+
+std::mt19937 rnd;
+string ffoTestString;
+const size_t ffoDelimSize = 128;
+vector<string> ffoDelim;
+
+void initFile(int len) {
+  std::uniform_int_distribution<uint32_t> validChar(1, 64);
+  file.clear();
+  while (len--) {
+    char ch = validChar(rnd);
+    if (ch == '\r') {
+      ch = '\n';
+    }
+    file.push_back(ch);
+  }
+}
+
+
+string generateString(int len) {
+  std::uniform_int_distribution<uint32_t> validChar(1, 255);  // no null-char
+  string ret;
+  while (len--) {
+    ret.push_back(validChar(rnd));
+  }
+  return ret;
+}
+
+void initDelims(int len) {
+  ffoDelim.clear();
+
+  string s(len - 1, '\0');  // find_first_of won't finish until last char
+  s.push_back('a');
+  ffoTestString = s;
+
+  for (size_t i = 0; i < ffoDelimSize; ++i) {
+    // most delimiter sets are pretty small, but occasionally there could
+    // be a big one.
+    auto n = rnd() % 8 + 1;
+    if (n == 8) {
+      n = 32;
+    }
+    auto s = generateString(n);
+    if (rnd() % 2) {
+      // ~half of tests will find a hit
+      s[rnd() % s.size()] = 'a';  // yes, this could mean 'a' is a duplicate
+    }
+    ffoDelim.push_back(s);
+  }
+}
+
+}  // anonymous namespace
+
+BENCHMARK(FindSingleCharMemchr, n) {
+  StringPiece haystack(str);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(haystack.find('b'));
+    char x = haystack[0];
+    doNotOptimizeAway(&x);
+  }
+}
+
+BENCHMARK_RELATIVE(FindSingleCharRange, n) {
+  const char c = 'b';
+  StringPiece haystack(str);
+  folly::StringPiece needle(&c, &c + 1);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(haystack.find(needle));
+    char x = haystack[0];
+    doNotOptimizeAway(&x);
+  }
+}
+
+BENCHMARK_DRAW_LINE();
+
+// it's useful to compare our custom implementations vs. the standard library
+inline size_t qfind_first_byte_of_std(const StringPiece haystack,
+                                      const StringPiece needles) {
+  return qfind_first_of(haystack, needles, asciiCaseSensitive);
+}
+
+template <class Func>
+void countHits(Func func, size_t n) {
+  StringPiece needles = "\r\n\1";
+  FOR_EACH_RANGE (i, 0, n) {
+    size_t p, n = 0;
+    for (StringPiece left = file;
+         (p = func(left, needles)) != StringPiece::npos;
+         left.advance(p + 1)) {
+      ++n;
+    }
+    doNotOptimizeAway(n);
+  }
+}
+
+template <class Func>
+void findFirstOfRange(StringPiece needles, Func func, size_t n) {
+  FOR_EACH_RANGE (i, 0, n) {
+    const StringPiece haystack = vstr[i % kVstrSize];
+    doNotOptimizeAway(func(haystack, needles));
+    char x = haystack[0];
+    doNotOptimizeAway(&x);
+  }
+}
+
+const string delims1 = "b";
+
+BENCHMARK(FindFirstOf1NeedlesBase, n) {
+  findFirstOfRange(delims1, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf1NeedlesNoSSE, n) {
+  findFirstOfRange(delims1, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf1NeedlesStd, n) {
+  findFirstOfRange(delims1, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf1NeedlesByteSet, n) {
+  findFirstOfRange(delims1, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+const string delims2 = "bc";
+
+BENCHMARK(FindFirstOf2NeedlesBase, n) {
+  findFirstOfRange(delims2, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf2NeedlesNoSSE, n) {
+  findFirstOfRange(delims2, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf2NeedlesStd, n) {
+  findFirstOfRange(delims2, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf2NeedlesByteSet, n) {
+  findFirstOfRange(delims2, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+const string delims4 = "bcde";
+
+BENCHMARK(FindFirstOf4NeedlesBase, n) {
+  findFirstOfRange(delims4, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf4NeedlesNoSSE, n) {
+  findFirstOfRange(delims4, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf4NeedlesStd, n) {
+  findFirstOfRange(delims4, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf4NeedlesByteSet, n) {
+  findFirstOfRange(delims4, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+const string delims8 = "0123456b";
+
+BENCHMARK(FindFirstOf8NeedlesBase, n) {
+  findFirstOfRange(delims8, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf8NeedlesNoSSE, n) {
+  findFirstOfRange(delims8, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf8NeedlesStd, n) {
+  findFirstOfRange(delims8, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf8NeedlesByteSet, n) {
+  findFirstOfRange(delims8, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+const string delims16 = "0123456789bcdefg";
+
+BENCHMARK(FindFirstOf16NeedlesBase, n) {
+  findFirstOfRange(delims16, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf16NeedlesNoSSE, n) {
+  findFirstOfRange(delims16, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf16NeedlesStd, n) {
+  findFirstOfRange(delims16, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf16NeedlesByteSet, n) {
+  findFirstOfRange(delims16, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+const string delims32 = "!bcdefghijklmnopqrstuvwxyz_012345";
+
+BENCHMARK(FindFirstOf32NeedlesBase, n) {
+  findFirstOfRange(delims32, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf32NeedlesNoSSE, n) {
+  findFirstOfRange(delims32, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf32NeedlesStd, n) {
+  findFirstOfRange(delims32, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf32NeedlesByteSet, n) {
+  findFirstOfRange(delims32, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+const string delims64 = "!bcdefghijklmnopqrstuvwxyz_"
+                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789$";
+
+BENCHMARK(FindFirstOf64NeedlesBase, n) {
+  findFirstOfRange(delims64, detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf64NeedlesNoSSE, n) {
+  findFirstOfRange(delims64, detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf64NeedlesStd, n) {
+  findFirstOfRange(delims64, qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOf64NeedlesByteSet, n) {
+  findFirstOfRange(delims64, detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+template <class Func>
+void findFirstOfRandom(Func func, size_t iters) {
+  for (size_t i = 0; i < iters; ++i) {
+    auto test = i % ffoDelim.size();
+    auto p = func(ffoTestString, ffoDelim[test]);
+    doNotOptimizeAway(p);
+  }
+}
+
+BENCHMARK(FindFirstOfRandomBase, n) {
+  findFirstOfRandom(detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOfRandomNoSSE, n) {
+  findFirstOfRandom(detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOfRandomStd, n) {
+  findFirstOfRandom(qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(FindFirstOfRandomByteSet, n) {
+  findFirstOfRandom(detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+BENCHMARK(CountDelimsBase, n) {
+  countHits(detail::qfind_first_byte_of, n);
+}
+
+BENCHMARK_RELATIVE(CountDelimsNoSSE, n) {
+  countHits(detail::qfind_first_byte_of_nosse, n);
+}
+
+BENCHMARK_RELATIVE(CountDelimsStd, n) {
+  countHits(qfind_first_byte_of_std, n);
+}
+
+BENCHMARK_RELATIVE(CountDelimsByteSet, n) {
+  countHits(detail::qfind_first_byte_of_byteset, n);
+}
+
+BENCHMARK_DRAW_LINE();
+
+BENCHMARK(FindFirstOfOffsetRange, n) {
+  StringPiece haystack(str);
+  folly::StringPiece needles("bc");
+  DCHECK_EQ(haystack.size() - 1, haystack.find_first_of(needles, 1)); // works!
+  FOR_EACH_RANGE (i, 0, n) {
+    size_t pos = i % 2; // not a constant to prevent optimization
+    doNotOptimizeAway(haystack.find_first_of(needles, pos));
+    char x = haystack[0];
+    doNotOptimizeAway(&x);
+  }
+}
+
+BENCHMARK_DRAW_LINE();
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  for (int len : {1, 8, 10, 16, 32, 64, 128, 256, 10*1024, 1024*1024}) {
+    initStr(len);
+    initDelims(len);
+    initFile(len);
+    runBenchmarks();
+  }
+  return 0;
+}
diff --git a/faux-folly/folly/test/RangeTest.cpp b/faux-folly/folly/test/RangeTest.cpp
new file mode 100644
index 0000000..8f0ecb3
--- /dev/null
+++ b/faux-folly/folly/test/RangeTest.cpp
@@ -0,0 +1,1195 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Kristina Holst (kholst@fb.com)
+// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
+
+#include <folly/Range.h>
+
+#include <sys/mman.h>
+#include <array>
+#include <cstdlib>
+#include <iterator>
+#include <limits>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <boost/range/concepts.hpp>
+#include <gtest/gtest.h>
+
+namespace {
+
+/* For some reason, GCC 4.9 doesn't implicitly cast 'const char[]' to
+ * 'const char*' to folly::StringPiece. This cast helps it along.
+ */
+inline const char* to_ccs(const char ra[])
+{
+    return static_cast<const char*>(ra);
+}
+
+} /* anonymous namespace */
+
+namespace folly { namespace detail {
+
+// declaration of functions in Range.cpp
+size_t qfind_first_byte_of_byteset(const StringPiece haystack,
+                                   const StringPiece needles);
+
+}}  // namespaces
+
+using namespace folly;
+using namespace std;
+
+static_assert(std::is_literal_type<StringPiece>::value, "");
+
+BOOST_CONCEPT_ASSERT((boost::RandomAccessRangeConcept<StringPiece>));
+
+TEST(StringPiece, All) {
+  const char* foo = "foo";
+  const char* foo2 = "foo";
+  string fooStr(foo);
+  string foo2Str(foo2);
+
+  // we expect the compiler to optimize things so that there's only one copy
+  // of the string literal "foo", even though we've got it in multiple places
+  EXPECT_EQ(foo, foo2);  // remember, this uses ==, not strcmp, so it's a ptr
+                         // comparison rather than lexical
+
+  // the string object creates copies though, so the c_str of these should be
+  // distinct
+  EXPECT_NE(fooStr.c_str(), foo2Str.c_str());
+
+  // test the basic StringPiece functionality
+  StringPiece s(foo);
+  EXPECT_EQ(s.size(), 3);
+
+  EXPECT_EQ(s.start(), foo);              // ptr comparison
+  EXPECT_NE(s.start(), fooStr.c_str());   // ptr comparison
+  EXPECT_NE(s.start(), foo2Str.c_str());  // ptr comparison
+
+  EXPECT_EQ(s.toString(), foo);              // lexical comparison
+  EXPECT_EQ(s.toString(), fooStr.c_str());   // lexical comparison
+  EXPECT_EQ(s.toString(), foo2Str.c_str());  // lexical comparison
+
+  EXPECT_EQ(s, foo);                      // lexical comparison
+  EXPECT_EQ(s, fooStr);                   // lexical comparison
+  EXPECT_EQ(s, foo2Str);                  // lexical comparison
+  EXPECT_EQ(foo, s);
+
+  // check using StringPiece to reference substrings
+  const char* foobarbaz = "foobarbaz";
+
+  // the full "foobarbaz"
+  s.reset(foobarbaz, strlen(foobarbaz));
+  EXPECT_EQ(s.size(), 9);
+  EXPECT_EQ(s.start(), foobarbaz);
+  EXPECT_EQ(s, to_ccs("foobarbaz"));
+
+  // only the 'foo'
+  s.assign(foobarbaz, foobarbaz + 3);
+  EXPECT_EQ(s.size(), 3);
+  EXPECT_EQ(s.start(), foobarbaz);
+  EXPECT_EQ(s, to_ccs("foo"));
+
+  // find
+  s.reset(foobarbaz, strlen(foobarbaz));
+  EXPECT_EQ(s.find("bar"), 3);
+  EXPECT_EQ(s.find("ba", 3), 3);
+  EXPECT_EQ(s.find("ba", 4), 6);
+  EXPECT_EQ(s.find("notfound"), StringPiece::npos);
+  EXPECT_EQ(s.find("notfound", 1), StringPiece::npos);
+  EXPECT_EQ(s.find("bar", 4), StringPiece::npos);  // starting position too far
+  // starting pos that is obviously past the end -- This works for std::string
+  EXPECT_EQ(s.toString().find("notfound", 55), StringPiece::npos);
+  EXPECT_EQ(s.find("z", s.size()), StringPiece::npos);
+  EXPECT_EQ(s.find("z", 55), StringPiece::npos);
+  // empty needle
+  EXPECT_EQ(s.find(""), std::string().find(""));
+  EXPECT_EQ(s.find(""), 0);
+
+  // single char finds
+  EXPECT_EQ(s.find('b'), 3);
+  EXPECT_EQ(s.find('b', 3), 3);
+  EXPECT_EQ(s.find('b', 4), 6);
+  EXPECT_EQ(s.find('o', 2), 2);
+  EXPECT_EQ(s.find('y'), StringPiece::npos);
+  EXPECT_EQ(s.find('y', 1), StringPiece::npos);
+  EXPECT_EQ(s.find('o', 4), StringPiece::npos);  // starting position too far
+  EXPECT_TRUE(s.contains('z'));
+  // starting pos that is obviously past the end -- This works for std::string
+  EXPECT_EQ(s.toString().find('y', 55), StringPiece::npos);
+  EXPECT_EQ(s.find('z', s.size()), StringPiece::npos);
+  EXPECT_EQ(s.find('z', 55), StringPiece::npos);
+  // null char
+  EXPECT_EQ(s.find('\0'), std::string().find('\0'));
+  EXPECT_EQ(s.find('\0'), StringPiece::npos);
+  EXPECT_FALSE(s.contains('\0'));
+
+  // single char rfinds
+  EXPECT_EQ(s.rfind('b'), 6);
+  EXPECT_EQ(s.rfind('y'), StringPiece::npos);
+  EXPECT_EQ(s.str().rfind('y'), StringPiece::npos);
+  EXPECT_EQ(ByteRange(s).rfind('b'), 6);
+  EXPECT_EQ(ByteRange(s).rfind('y'), StringPiece::npos);
+  // null char
+  EXPECT_EQ(s.rfind('\0'), s.str().rfind('\0'));
+  EXPECT_EQ(s.rfind('\0'), StringPiece::npos);
+
+  // find_first_of
+  s.reset(foobarbaz, strlen(foobarbaz));
+  EXPECT_EQ(s.find_first_of("bar"), 3);
+  EXPECT_EQ(s.find_first_of("ba", 3), 3);
+  EXPECT_EQ(s.find_first_of("ba", 4), 4);
+  EXPECT_TRUE(s.contains("bar"));
+  EXPECT_EQ(s.find_first_of("xyxy"), StringPiece::npos);
+  EXPECT_EQ(s.find_first_of("xyxy", 1), StringPiece::npos);
+  EXPECT_FALSE(s.contains("xyxy"));
+  // starting position too far
+  EXPECT_EQ(s.find_first_of("foo", 4), StringPiece::npos);
+  // starting pos that is obviously past the end -- This works for std::string
+  EXPECT_EQ(s.toString().find_first_of("xyxy", 55), StringPiece::npos);
+  EXPECT_EQ(s.find_first_of("z", s.size()), StringPiece::npos);
+  EXPECT_EQ(s.find_first_of("z", 55), StringPiece::npos);
+  // empty needle. Note that this returns npos, while find() returns 0!
+  EXPECT_EQ(s.find_first_of(""), std::string().find_first_of(""));
+  EXPECT_EQ(s.find_first_of(""), StringPiece::npos);
+
+  // single char find_first_ofs
+  EXPECT_EQ(s.find_first_of('b'), 3);
+  EXPECT_EQ(s.find_first_of('b', 3), 3);
+  EXPECT_EQ(s.find_first_of('b', 4), 6);
+  EXPECT_EQ(s.find_first_of('o', 2), 2);
+  EXPECT_EQ(s.find_first_of('y'), StringPiece::npos);
+  EXPECT_EQ(s.find_first_of('y', 1), StringPiece::npos);
+  // starting position too far
+  EXPECT_EQ(s.find_first_of('o', 4), StringPiece::npos);
+  // starting pos that is obviously past the end -- This works for std::string
+  EXPECT_EQ(s.toString().find_first_of('y', 55), StringPiece::npos);
+  EXPECT_EQ(s.find_first_of('z', s.size()), StringPiece::npos);
+  EXPECT_EQ(s.find_first_of('z', 55), StringPiece::npos);
+  // null char
+  EXPECT_EQ(s.find_first_of('\0'), std::string().find_first_of('\0'));
+  EXPECT_EQ(s.find_first_of('\0'), StringPiece::npos);
+
+  // just "barbaz"
+  s.reset(foobarbaz + 3, strlen(foobarbaz + 3));
+  EXPECT_EQ(s.size(), 6);
+  EXPECT_EQ(s.start(), foobarbaz + 3);
+  EXPECT_EQ(s, to_ccs("barbaz"));
+
+  // just "bar"
+  s.reset(foobarbaz + 3, 3);
+  EXPECT_EQ(s.size(), 3);
+  EXPECT_EQ(s, to_ccs("bar"));
+
+  // clear
+  s.clear();
+  EXPECT_EQ(s.toString(), "");
+
+  // test an empty StringPiece
+  StringPiece s2;
+  EXPECT_EQ(s2.size(), 0);
+
+  // Test comparison operators
+  foo = "";
+  EXPECT_LE(s, foo);
+  EXPECT_LE(foo, s);
+  EXPECT_GE(s, foo);
+  EXPECT_GE(foo, s);
+  EXPECT_EQ(s, foo);
+  EXPECT_EQ(foo, s);
+
+  foo = "abc";
+  EXPECT_LE(s, foo);
+  EXPECT_LT(s, foo);
+  EXPECT_GE(foo, s);
+  EXPECT_GT(foo, s);
+  EXPECT_NE(s, foo);
+
+  EXPECT_LE(s, s);
+  EXPECT_LE(s, s);
+  EXPECT_GE(s, s);
+  EXPECT_GE(s, s);
+  EXPECT_EQ(s, s);
+  EXPECT_EQ(s, s);
+
+  s = "abc";
+  s2 = "abc";
+  EXPECT_LE(s, s2);
+  EXPECT_LE(s2, s);
+  EXPECT_GE(s, s2);
+  EXPECT_GE(s2, s);
+  EXPECT_EQ(s, s2);
+  EXPECT_EQ(s2, s);
+}
+
+template <class T>
+void expectLT(const T& a, const T& b) {
+  EXPECT_TRUE(a < b);
+  EXPECT_TRUE(a <= b);
+  EXPECT_FALSE(a == b);
+  EXPECT_FALSE(a >= b);
+  EXPECT_FALSE(a > b);
+
+  EXPECT_FALSE(b < a);
+  EXPECT_FALSE(b <= a);
+  EXPECT_TRUE(b >= a);
+  EXPECT_TRUE(b > a);
+}
+
+template <class T>
+void expectEQ(const T& a, const T& b) {
+  EXPECT_FALSE(a < b);
+  EXPECT_TRUE(a <= b);
+  EXPECT_TRUE(a == b);
+  EXPECT_TRUE(a >= b);
+  EXPECT_FALSE(a > b);
+}
+
+TEST(StringPiece, EightBitComparisons) {
+  char values[] = {'\x00', '\x20', '\x40', '\x7f', '\x80', '\xc0', '\xff'};
+  constexpr size_t count = sizeof(values) / sizeof(values[0]);
+  for (size_t i = 0; i < count; ++i) {
+    std::string a(1, values[i]);
+    // Defeat copy-on-write
+    std::string aCopy(a.data(), a.size());
+    expectEQ(a, aCopy);
+    expectEQ(StringPiece(a), StringPiece(aCopy));
+
+    for (size_t j = i + 1; j < count; ++j) {
+      std::string b(1, values[j]);
+      expectLT(a, b);
+      expectLT(StringPiece(a), StringPiece(b));
+    }
+  }
+}
+
+TEST(StringPiece, ToByteRange) {
+  StringPiece a("hello");
+  ByteRange b(a);
+  EXPECT_EQ(static_cast<const void*>(a.begin()),
+            static_cast<const void*>(b.begin()));
+  EXPECT_EQ(static_cast<const void*>(a.end()),
+            static_cast<const void*>(b.end()));
+
+  // and convert back again
+  StringPiece c(b);
+  EXPECT_EQ(a.begin(), c.begin());
+  EXPECT_EQ(a.end(), c.end());
+}
+
+TEST(StringPiece, InvalidRange) {
+  StringPiece a("hello");
+  EXPECT_EQ(a, a.subpiece(0, 10));
+  EXPECT_EQ(StringPiece("ello"), a.subpiece(1));
+  EXPECT_EQ(StringPiece("ello"), a.subpiece(1, std::string::npos));
+  EXPECT_EQ(StringPiece("ell"), a.subpiece(1, 3));
+  EXPECT_THROW(a.subpiece(6, 7), std::out_of_range);
+  EXPECT_THROW(a.subpiece(6), std::out_of_range);
+
+  std::string b("hello");
+  EXPECT_EQ(a, StringPiece(b, 0, 10));
+  EXPECT_EQ(to_ccs("ello"), a.subpiece(1));
+  EXPECT_EQ(to_ccs("ello"), a.subpiece(1, std::string::npos));
+  EXPECT_EQ(to_ccs("ell"), a.subpiece(1, 3));
+  EXPECT_THROW(a.subpiece(6, 7), std::out_of_range);
+  EXPECT_THROW(a.subpiece(6), std::out_of_range);
+}
+
+#if FOLLY_HAVE_CONSTEXPR_STRLEN
+constexpr char helloArray[] = "hello";
+
+TEST(StringPiece, Constexpr) {
+  constexpr StringPiece hello1("hello");
+  EXPECT_EQ("hello", hello1);
+
+  constexpr StringPiece hello2(helloArray);
+  EXPECT_EQ("hello", hello2);
+}
+#endif
+
+TEST(StringPiece, Prefix) {
+  StringPiece a("hello");
+  EXPECT_TRUE(a.startsWith(""));
+  EXPECT_TRUE(a.startsWith("h"));
+  EXPECT_TRUE(a.startsWith('h'));
+  EXPECT_TRUE(a.startsWith("hello"));
+  EXPECT_FALSE(a.startsWith("hellox"));
+  EXPECT_FALSE(a.startsWith('x'));
+  EXPECT_FALSE(a.startsWith("x"));
+
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removePrefix(""));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removePrefix("h"));
+    EXPECT_EQ(to_ccs("ello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removePrefix('h'));
+    EXPECT_EQ(to_ccs("ello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removePrefix("hello"));
+    EXPECT_EQ(to_ccs(""), b);
+  }
+  {
+    auto b = a;
+    EXPECT_FALSE(b.removePrefix("hellox"));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_FALSE(b.removePrefix("x"));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_FALSE(b.removePrefix('x'));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+}
+
+TEST(StringPiece, Suffix) {
+  StringPiece a("hello");
+  EXPECT_TRUE(a.endsWith(""));
+  EXPECT_TRUE(a.endsWith("o"));
+  EXPECT_TRUE(a.endsWith('o'));
+  EXPECT_TRUE(a.endsWith("hello"));
+  EXPECT_FALSE(a.endsWith("xhello"));
+  EXPECT_FALSE(a.endsWith("x"));
+  EXPECT_FALSE(a.endsWith('x'));
+
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removeSuffix(""));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removeSuffix("o"));
+    EXPECT_EQ(to_ccs("hell"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removeSuffix('o'));
+    EXPECT_EQ(to_ccs("hell"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_TRUE(b.removeSuffix("hello"));
+    EXPECT_EQ(to_ccs(""), b);
+  }
+  {
+    auto b = a;
+    EXPECT_FALSE(b.removeSuffix("xhello"));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_FALSE(b.removeSuffix("x"));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+  {
+    auto b = a;
+    EXPECT_FALSE(b.removeSuffix('x'));
+    EXPECT_EQ(to_ccs("hello"), b);
+  }
+}
+
+TEST(StringPiece, PrefixEmpty) {
+  StringPiece a;
+  EXPECT_TRUE(a.startsWith(""));
+  EXPECT_FALSE(a.startsWith("a"));
+  EXPECT_FALSE(a.startsWith('a'));
+  EXPECT_TRUE(a.removePrefix(""));
+  EXPECT_EQ(to_ccs(""), a);
+  EXPECT_FALSE(a.removePrefix("a"));
+  EXPECT_EQ(to_ccs(""), a);
+  EXPECT_FALSE(a.removePrefix('a'));
+  EXPECT_EQ(to_ccs(""), a);
+}
+
+TEST(StringPiece, SuffixEmpty) {
+  StringPiece a;
+  EXPECT_TRUE(a.endsWith(""));
+  EXPECT_FALSE(a.endsWith("a"));
+  EXPECT_FALSE(a.endsWith('a'));
+  EXPECT_TRUE(a.removeSuffix(""));
+  EXPECT_EQ(to_ccs(""), a);
+  EXPECT_FALSE(a.removeSuffix("a"));
+  EXPECT_EQ(to_ccs(""), a);
+  EXPECT_FALSE(a.removeSuffix('a'));
+  EXPECT_EQ(to_ccs(""), a);
+}
+
+TEST(StringPiece, split_step_char_delimiter) {
+  //              0         1         2
+  //              012345678901234567890123456
+  auto const s = "this is just  a test string";
+  auto const e = std::next(s, std::strlen(s));
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto x = p.split_step(' ');
+  EXPECT_EQ(std::next(s, 5), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("this"), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(std::next(s, 8), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("is"), x);
+
+  x = p.split_step('u');
+  EXPECT_EQ(std::next(s, 10), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("j"), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(std::next(s, 13), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("st"), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(std::next(s, 14), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs(""), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(std::next(s, 16), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("a"), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(std::next(s, 21), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("test"), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(e, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("string"), x);
+
+  x = p.split_step(' ');
+  EXPECT_EQ(e, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs(""), x);
+}
+
+TEST(StringPiece, split_step_range_delimiter) {
+  //              0         1         2         3
+  //              0123456789012345678901234567890123
+  auto const s = "this  is  just    a   test  string";
+  auto const e = std::next(s, std::strlen(s));
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto x = p.split_step("  ");
+  EXPECT_EQ(std::next(s, 6), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("this"), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(std::next(s, 10), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("is"), x);
+
+  x = p.split_step("u");
+  EXPECT_EQ(std::next(s, 12), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("j"), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(std::next(s, 16), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("st"), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(std::next(s, 18), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs(""), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(std::next(s, 21), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("a"), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(std::next(s, 28), p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs(" test"), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(e, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs("string"), x);
+
+  x = p.split_step("  ");
+  EXPECT_EQ(e, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs(""), x);
+
+  x = p.split_step(" ");
+  EXPECT_EQ(e, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(to_ccs(""), x);
+}
+
+void split_step_with_process_noop(folly::StringPiece) {}
+
+TEST(StringPiece, split_step_with_process_char_delimiter) {
+  //              0         1         2
+  //              012345678901234567890123456
+  auto const s = "this is just  a test string";
+  auto const e = std::next(s, std::strlen(s));
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  EXPECT_EQ(1, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 5), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("this"), x);
+    return 1;
+  })));
+
+  EXPECT_EQ(2, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 8), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("is"), x);
+    return 2;
+  })));
+
+  EXPECT_EQ(3, (p.split_step('u', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 10), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("j"), x);
+    return 3;
+  })));
+
+  EXPECT_EQ(4, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 13), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("st"), x);
+    return 4;
+  })));
+
+  EXPECT_EQ(5, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 14), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs(""), x);
+    return 5;
+  })));
+
+  EXPECT_EQ(6, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 16), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("a"), x);
+    return 6;
+  })));
+
+  EXPECT_EQ(7, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 21), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("test"), x);
+    return 7;
+  })));
+
+  EXPECT_EQ(8, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(e, p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("string"), x);
+    return 8;
+  })));
+
+  EXPECT_EQ(9, (p.split_step(' ', [&](folly::StringPiece x) {
+    EXPECT_EQ(e, p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs(""), x);
+    return 9;
+  })));
+
+  EXPECT_TRUE((std::is_same<
+    void,
+    decltype(p.split_step(' ', split_step_with_process_noop))
+  >::value));
+
+  EXPECT_NO_THROW(p.split_step(' ', split_step_with_process_noop));
+}
+
+TEST(StringPiece, split_step_with_process_range_delimiter) {
+  //              0         1         2         3
+  //              0123456789012345678901234567890123
+  auto const s = "this  is  just    a   test  string";
+  auto const e = std::next(s, std::strlen(s));
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  EXPECT_EQ(1, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 6), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("this"), x);
+    return 1;
+  })));
+
+  EXPECT_EQ(2, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 10), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("is"), x);
+    return 2;
+  })));
+
+  EXPECT_EQ(3, (p.split_step("u", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 12), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("j"), x);
+    return 3;
+  })));
+
+  EXPECT_EQ(4, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 16), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("st"), x);
+    return 4;
+  })));
+
+  EXPECT_EQ(5, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 18), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs(""), x);
+    return 5;
+  })));
+
+  EXPECT_EQ(6, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 21), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("a"), x);
+    return 6;
+  })));
+
+  EXPECT_EQ(7, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(std::next(s, 28), p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs(" test"), x);
+    return 7;
+  })));
+
+  EXPECT_EQ(8, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(e, p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs("string"), x);
+    return 8;
+  })));
+
+  EXPECT_EQ(9, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(e, p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs(""), x);
+    return 9;
+  })));
+
+  EXPECT_EQ(10, (p.split_step("  ", [&](folly::StringPiece x) {
+    EXPECT_EQ(e, p.begin());
+    EXPECT_EQ(e, p.end());
+    EXPECT_EQ(to_ccs(""), x);
+    return 10;
+  })));
+
+  EXPECT_TRUE((std::is_same<
+    void,
+    decltype(p.split_step(' ', split_step_with_process_noop))
+  >::value));
+
+  EXPECT_NO_THROW(p.split_step(' ', split_step_with_process_noop));
+}
+
+TEST(StringPiece, split_step_with_process_char_delimiter_additional_args) {
+  //              0         1         2
+  //              012345678901234567890123456
+  auto const s = "this is just  a test string";
+  auto const e = std::next(s, std::strlen(s));
+  auto const delimiter = ' ';
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto const functor = [](
+    folly::StringPiece s,
+    folly::StringPiece expected
+  ) {
+    EXPECT_EQ(expected, s);
+    return expected;
+  };
+
+  auto const checker = [&](folly::StringPiece expected) {
+    EXPECT_EQ(expected, p.split_step(delimiter, functor, expected));
+  };
+
+  checker("this");
+  checker("is");
+  checker("just");
+  checker("");
+  checker("a");
+  checker("test");
+  checker("string");
+  checker("");
+  checker("");
+
+  EXPECT_TRUE(p.empty());
+}
+
+TEST(StringPiece, split_step_with_process_range_delimiter_additional_args) {
+  //              0         1         2         3
+  //              0123456789012345678901234567890123
+  auto const s = "this  is  just    a   test  string";
+  auto const e = std::next(s, std::strlen(s));
+  auto const delimiter = "  ";
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto const functor = [](
+    folly::StringPiece s,
+    folly::StringPiece expected
+  ) {
+    EXPECT_EQ(expected, s);
+    return expected;
+  };
+
+  auto const checker = [&](folly::StringPiece expected) {
+    EXPECT_EQ(expected, p.split_step(delimiter, functor, expected));
+  };
+
+  checker("this");
+  checker("is");
+  checker("just");
+  checker("");
+  checker("a");
+  checker(" test");
+  checker("string");
+  checker("");
+  checker("");
+
+  EXPECT_TRUE(p.empty());
+}
+
+TEST(StringPiece, NoInvalidImplicitConversions) {
+  struct IsString {
+    bool operator()(folly::Range<int*>) { return false; }
+    bool operator()(folly::StringPiece) { return true; }
+  };
+
+  std::string s = "hello";
+  EXPECT_TRUE(IsString()(s));
+}
+
+TEST(qfind, UInt32_Ranges) {
+  vector<uint32_t> a({1, 2, 3, 260, 5});
+  vector<uint32_t> b({2, 3, 4});
+
+  auto a_range = folly::Range<const uint32_t*>(&a[0], a.size());
+  auto b_range = folly::Range<const uint32_t*>(&b[0], b.size());
+
+  EXPECT_EQ(qfind(a_range, b_range), string::npos);
+
+  a[3] = 4;
+  EXPECT_EQ(qfind(a_range, b_range), 1);
+}
+
+template <typename NeedleFinder>
+class NeedleFinderTest : public ::testing::Test {
+ public:
+  static size_t find_first_byte_of(StringPiece haystack, StringPiece needles) {
+    return NeedleFinder::find_first_byte_of(haystack, needles);
+  }
+};
+
+struct SseNeedleFinder {
+  static size_t find_first_byte_of(StringPiece haystack, StringPiece needles) {
+    // This will only use the SSE version if it is supported on this CPU
+    // (selected using ifunc).
+    return detail::qfind_first_byte_of(haystack, needles);
+  }
+};
+
+struct NoSseNeedleFinder {
+  static size_t find_first_byte_of(StringPiece haystack, StringPiece needles) {
+    return detail::qfind_first_byte_of_nosse(haystack, needles);
+  }
+};
+
+struct ByteSetNeedleFinder {
+  static size_t find_first_byte_of(StringPiece haystack, StringPiece needles) {
+    return detail::qfind_first_byte_of_byteset(haystack, needles);
+  }
+};
+
+typedef ::testing::Types<SseNeedleFinder,
+                         NoSseNeedleFinder,
+                         ByteSetNeedleFinder> NeedleFinders;
+TYPED_TEST_CASE(NeedleFinderTest, NeedleFinders);
+
+TYPED_TEST(NeedleFinderTest, Null) {
+  { // null characters in the string
+    string s(10, char(0));
+    s[5] = 'b';
+    string delims("abc");
+    EXPECT_EQ(5, this->find_first_byte_of(s, delims));
+  }
+  { // null characters in delim
+    string s("abc");
+    string delims(10, char(0));
+    delims[3] = 'c';
+    delims[7] = 'b';
+    EXPECT_EQ(1, this->find_first_byte_of(s, delims));
+  }
+  { // range not terminated by null character
+    string buf = "abcdefghijklmnopqrstuvwxyz";
+    StringPiece s(buf.data() + 5, 3);
+    StringPiece delims("z");
+    EXPECT_EQ(string::npos, this->find_first_byte_of(s, delims));
+  }
+}
+
+TYPED_TEST(NeedleFinderTest, DelimDuplicates) {
+  string delims(1000, 'b');
+  EXPECT_EQ(1, this->find_first_byte_of("abc", delims));
+  EXPECT_EQ(string::npos, this->find_first_byte_of("ac", delims));
+}
+
+TYPED_TEST(NeedleFinderTest, Empty) {
+  string a = "abc";
+  string b = "";
+  EXPECT_EQ(string::npos, this->find_first_byte_of(a, b));
+  EXPECT_EQ(string::npos, this->find_first_byte_of(b, a));
+  EXPECT_EQ(string::npos, this->find_first_byte_of(b, b));
+}
+
+TYPED_TEST(NeedleFinderTest, Unaligned) {
+  // works correctly even if input buffers are not 16-byte aligned
+  string s = "0123456789ABCDEFGH";
+  for (size_t i = 0; i < s.size(); ++i) {
+    StringPiece a(s.c_str() + i);
+    for (size_t j = 0; j < s.size(); ++j) {
+      StringPiece b(s.c_str() + j);
+      EXPECT_EQ((i > j) ? 0 : j - i, this->find_first_byte_of(a, b));
+    }
+  }
+}
+
+// for some algorithms (specifically those that create a set of needles),
+// we check for the edge-case of _all_ possible needles being sought.
+TYPED_TEST(NeedleFinderTest, Needles256) {
+  string needles;
+  const auto minValue = std::numeric_limits<StringPiece::value_type>::min();
+  const auto maxValue = std::numeric_limits<StringPiece::value_type>::max();
+  // make the size ~big to avoid any edge-case branches for tiny haystacks
+  const int haystackSize = 50;
+  for (size_t i = minValue; i <= maxValue; i++) {  // <=
+    needles.push_back(i);
+  }
+  EXPECT_EQ(StringPiece::npos, this->find_first_byte_of("", needles));
+  for (size_t i = minValue; i <= maxValue; i++) {
+    EXPECT_EQ(0, this->find_first_byte_of(string(haystackSize, i), needles));
+  }
+
+  needles.append("these are redundant characters");
+  EXPECT_EQ(StringPiece::npos, this->find_first_byte_of("", needles));
+  for (size_t i = minValue; i <= maxValue; i++) {
+    EXPECT_EQ(0, this->find_first_byte_of(string(haystackSize, i), needles));
+  }
+}
+
+TYPED_TEST(NeedleFinderTest, Base) {
+  for (size_t i = 0; i < 32; ++i) {
+    for (int j = 0; j < 32; ++j) {
+      string s = string(i, 'X') + "abca" + string(i, 'X');
+      string delims = string(j, 'Y') + "a" + string(j, 'Y');
+      EXPECT_EQ(i, this->find_first_byte_of(s, delims));
+    }
+  }
+}
+
+const size_t kPageSize = 4096;
+// Updates contents so that any read accesses past the last byte will
+// cause a SIGSEGV.  It accomplishes this by changing access to the page that
+// begins immediately after the end of the contents (as allocators and mmap()
+// all operate on page boundaries, this is a reasonable assumption).
+// This function will also initialize buf, which caller must free().
+void createProtectedBuf(StringPiece& contents, char** buf) {
+  ASSERT_LE(contents.size(), kPageSize);
+  const size_t kSuccess = 0;
+  if (kSuccess != posix_memalign((void**)buf, kPageSize, 4 * kPageSize)) {
+    ASSERT_FALSE(true);
+  }
+  mprotect(*buf + kPageSize, kPageSize, PROT_NONE);
+  size_t newBegin = kPageSize - contents.size();
+  memcpy(*buf + newBegin, contents.data(), contents.size());
+  contents.reset(*buf + newBegin, contents.size());
+}
+
+void freeProtectedBuf(char* buf) {
+  mprotect(buf + kPageSize, kPageSize, PROT_READ | PROT_WRITE);
+  free(buf);
+}
+
+TYPED_TEST(NeedleFinderTest, NoSegFault) {
+  const string base = string(32, 'a') + string("b");
+  const string delims = string(32, 'c') + string("b");
+  for (int i = 0; i <= 32; i++) {
+    for (int j = 0; j <= 33; j++) {
+      for (int shouldFind = 0; shouldFind <= 1; ++shouldFind) {
+        StringPiece s1(base);
+        s1.advance(i);
+        ASSERT_TRUE(!s1.empty());
+        if (!shouldFind) {
+          s1.pop_back();
+        }
+        StringPiece s2(delims);
+        s2.advance(j);
+        char* buf1;
+        char* buf2;
+        createProtectedBuf(s1, &buf1);
+        createProtectedBuf(s2, &buf2);
+        // printf("s1: '%s' (%ld) \ts2: '%s' (%ld)\n",
+        //        string(s1.data(), s1.size()).c_str(), s1.size(),
+        //        string(s2.data(), s2.size()).c_str(), s2.size());
+        auto r1 = this->find_first_byte_of(s1, s2);
+        auto f1 = std::find_first_of(s1.begin(), s1.end(),
+                                     s2.begin(), s2.end());
+        auto e1 = (f1 == s1.end()) ? StringPiece::npos : f1 - s1.begin();
+        EXPECT_EQ(r1, e1);
+        auto r2 = this->find_first_byte_of(s2, s1);
+        auto f2 = std::find_first_of(s2.begin(), s2.end(),
+                                     s1.begin(), s1.end());
+        auto e2 = (f2 == s2.end()) ? StringPiece::npos : f2 - s2.begin();
+        EXPECT_EQ(r2, e2);
+        freeProtectedBuf(buf1);
+        freeProtectedBuf(buf2);
+      }
+    }
+  }
+}
+
+TEST(NonConstTest, StringPiece) {
+  std::string hello("hello");
+  MutableStringPiece sp(&hello.front(), hello.size());
+  sp[0] = 'x';
+  EXPECT_EQ("xello", hello);
+  {
+    StringPiece s(sp);
+    EXPECT_EQ(to_ccs("xello"), s);
+  }
+  {
+    ByteRange r1(sp);
+    MutableByteRange r2(sp);
+  }
+}
+
+template<class C>
+void testRangeFunc(C&& x, size_t n) {
+  const auto& cx = x;
+  // type, conversion checks
+  Range<int*> r1 = range(std::forward<C>(x));
+  Range<const int*> r2 = range(std::forward<C>(x));
+  Range<const int*> r3 = range(cx);
+  Range<const int*> r5 = range(std::move(cx));
+  EXPECT_EQ(r1.begin(), &x[0]);
+  EXPECT_EQ(r1.end(), &x[n]);
+  EXPECT_EQ(n, r1.size());
+  EXPECT_EQ(n, r2.size());
+  EXPECT_EQ(n, r3.size());
+  EXPECT_EQ(n, r5.size());
+}
+
+TEST(RangeFunc, Vector) {
+  std::vector<int> x;
+  testRangeFunc(x, 0);
+  x.push_back(2);
+  testRangeFunc(x, 1);
+  testRangeFunc(std::vector<int>{1, 2}, 2);
+}
+
+TEST(RangeFunc, Array) {
+  std::array<int, 3> x;
+  testRangeFunc(x, 3);
+}
+
+TEST(RangeFunc, CArray) {
+  int x[] {1, 2, 3, 4};
+  testRangeFunc(x, 4);
+}
+
+std::string get_rand_str(size_t size,
+                         std::uniform_int_distribution<>& dist,
+                         std::mt19937& gen) {
+  std::string ret(size, '\0');
+  for (size_t i = 0; i < size; ++i) {
+    ret[i] = static_cast<char>(dist(gen));
+  }
+
+  return ret;
+}
+
+namespace folly {
+bool operator==(MutableStringPiece mp, StringPiece sp) {
+  return mp.compare(sp) == 0;
+}
+
+bool operator==(StringPiece sp, MutableStringPiece mp) {
+  return mp.compare(sp) == 0;
+}
+}
+
+TEST(ReplaceAt, exhaustiveTest) {
+  char input[] = "this is nice and long input";
+  auto msp = MutableStringPiece(input);
+  auto str = std::string(input);
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<> dist('a', 'z');
+
+  for (int i=0; i < 100; ++i) {
+    for (size_t j = 1; j <= msp.size(); ++j) {
+      auto replacement = get_rand_str(j, dist, gen);
+      for (size_t pos = 0; pos < msp.size() - j; ++pos) {
+        msp.replaceAt(pos, replacement);
+        str.replace(pos, replacement.size(), replacement);
+        EXPECT_EQ(msp.compare(str), 0);
+      }
+    }
+  }
+
+  // too far
+  EXPECT_EQ(msp.replaceAt(msp.size() - 2, StringPiece("meh")), false);
+}
+
+TEST(ReplaceAll, basicTest) {
+  char input[] = "this is nice and long input";
+  auto orig = std::string(input);
+  auto msp = MutableStringPiece(input);
+
+  EXPECT_EQ(msp.replaceAll("is", "si"), 2);
+  EXPECT_EQ("thsi si nice and long input", msp);
+  EXPECT_EQ(msp.replaceAll("si", "is"), 2);
+  EXPECT_EQ(msp, orig);
+
+  EXPECT_EQ(msp.replaceAll("abcd", "efgh"), 0); // nothing to replace
+  EXPECT_EQ(msp, orig);
+
+  // at the very beginning
+  EXPECT_EQ(msp.replaceAll("this", "siht"), 1);
+  EXPECT_EQ("siht is nice and long input", msp);
+  EXPECT_EQ(msp.replaceAll("siht", "this"), 1);
+  EXPECT_EQ(msp, orig);
+
+  // at the very end
+  EXPECT_EQ(msp.replaceAll("input", "soput"), 1);
+  EXPECT_EQ("this is nice and long soput", msp);
+  EXPECT_EQ(msp.replaceAll("soput", "input"), 1);
+  EXPECT_EQ(msp, orig);
+
+  // all spaces
+  EXPECT_EQ(msp.replaceAll(" ", "@"), 5);
+  EXPECT_EQ("this@is@nice@and@long@input", msp);
+  EXPECT_EQ(msp.replaceAll("@", " "), 5);
+  EXPECT_EQ(msp, orig);
+}
+
+TEST(ReplaceAll, randomTest) {
+  char input[] = "abcdefghijklmnoprstuwqz"; // no pattern repeata inside
+  auto orig = std::string(input);
+  auto msp = MutableStringPiece(input);
+
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<> dist('A', 'Z');
+
+  for (int i=0; i < 100; ++i) {
+    for (size_t j = 1; j <= orig.size(); ++j) {
+      auto replacement = get_rand_str(j, dist, gen);
+      for (size_t pos = 0; pos < msp.size() - j; ++pos) {
+        auto piece = orig.substr(pos, j);
+        EXPECT_EQ(msp.replaceAll(piece, replacement), 1);
+        EXPECT_EQ(msp.find(replacement), pos);
+        EXPECT_EQ(msp.replaceAll(replacement, piece), 1);
+        EXPECT_EQ(msp, orig);
+      }
+    }
+  }
+}
+
+TEST(ReplaceAll, BadArg) {
+  int count = 0;
+  auto fst = "longer";
+  auto snd = "small";
+  char input[] = "meh meh meh";
+  auto all =  MutableStringPiece(input);
+
+  try {
+    all.replaceAll(fst, snd);
+  } catch (std::invalid_argument&) {
+    ++count;
+  }
+
+  try {
+    all.replaceAll(snd, fst);
+  } catch (std::invalid_argument&) {
+    ++count;
+  }
+
+  EXPECT_EQ(count, 2);
+}
+
+TEST(Range, Constructors) {
+  vector<int> c = {1, 2, 3};
+  typedef Range<vector<int>::iterator> RangeType;
+  typedef Range<vector<int>::const_iterator> ConstRangeType;
+  RangeType cr(c.begin(), c.end());
+  auto subpiece1 = ConstRangeType(cr, 1, 5);
+  auto subpiece2 = ConstRangeType(cr, 1);
+  EXPECT_EQ(subpiece1.size(), 2);
+  EXPECT_EQ(subpiece1.begin(), subpiece2.begin());
+  EXPECT_EQ(subpiece1.end(), subpiece2.end());
+}
diff --git a/faux-folly/folly/test/ScopeGuardTest.cpp b/faux-folly/folly/test/ScopeGuardTest.cpp
new file mode 100644
index 0000000..44ddaf9
--- /dev/null
+++ b/faux-folly/folly/test/ScopeGuardTest.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2015 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/ScopeGuard.h>
+#include <folly/Portability.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+
+#include <functional>
+#include <stdexcept>
+
+using folly::ScopeGuard;
+using folly::makeGuard;
+using std::vector;
+
+double returnsDouble() {
+  return 0.0;
+}
+
+class MyFunctor {
+ public:
+  explicit MyFunctor(int* ptr) : ptr_(ptr) {}
+
+  void operator()() {
+    ++*ptr_;
+  }
+
+ private:
+  int* ptr_;
+};
+
+TEST(ScopeGuard, DifferentWaysToBind) {
+  {
+    // There is implicit conversion from func pointer
+    // double (*)() to function<void()>.
+    ScopeGuard g = makeGuard(returnsDouble);
+  }
+
+  vector<int> v;
+  void (vector<int>::*push_back)(int const&) = &vector<int>::push_back;
+
+  v.push_back(1);
+  {
+    // binding to member function.
+    ScopeGuard g = makeGuard(std::bind(&vector<int>::pop_back, &v));
+  }
+  EXPECT_EQ(0, v.size());
+
+  {
+    // bind member function with args. v is passed-by-value!
+    ScopeGuard g = makeGuard(std::bind(push_back, v, 2));
+  }
+  EXPECT_EQ(0, v.size()); // push_back happened on a copy of v... fail!
+
+  // pass in an argument by pointer so to avoid copy.
+  {
+    ScopeGuard g = makeGuard(std::bind(push_back, &v, 4));
+  }
+  EXPECT_EQ(1, v.size());
+
+  {
+    // pass in an argument by reference so to avoid copy.
+    ScopeGuard g = makeGuard(std::bind(push_back, std::ref(v), 4));
+  }
+  EXPECT_EQ(2, v.size());
+
+  // lambda with a reference to v
+  {
+    ScopeGuard g = makeGuard([&] { v.push_back(5); });
+  }
+  EXPECT_EQ(3, v.size());
+
+  // lambda with a copy of v
+  {
+    ScopeGuard g = makeGuard([v] () mutable { v.push_back(6); });
+  }
+  EXPECT_EQ(3, v.size());
+
+  // functor object
+  int n = 0;
+  {
+    MyFunctor f(&n);
+    ScopeGuard g = makeGuard(f);
+  }
+  EXPECT_EQ(1, n);
+
+  // temporary functor object
+  n = 0;
+  {
+    ScopeGuard g = makeGuard(MyFunctor(&n));
+  }
+  EXPECT_EQ(1, n);
+
+  // Use auto instead of ScopeGuard
+  n = 2;
+  {
+    auto g = makeGuard(MyFunctor(&n));
+  }
+  EXPECT_EQ(3, n);
+
+  // Use const auto& instead of ScopeGuard
+  n = 10;
+  {
+    const auto& g = makeGuard(MyFunctor(&n));
+  }
+  EXPECT_EQ(11, n);
+}
+
+TEST(ScopeGuard, GuardException) {
+  EXPECT_DEATH({
+    ScopeGuard g = makeGuard([&] {
+      throw std::runtime_error("destructors should never throw!");
+    });
+  },
+  "destructors should never throw!"
+  );
+}
+
+/**
+ * Add an integer to a vector iff it was inserted into the
+ * db successfuly. Here is a schematic of how you would accomplish
+ * this with scope guard.
+ */
+void testUndoAction(bool failure) {
+  vector<int64_t> v;
+  { // defines a "mini" scope
+
+    // be optimistic and insert this into memory
+    v.push_back(1);
+
+    // The guard is triggered to undo the insertion unless dismiss() is called.
+    ScopeGuard guard = makeGuard([&] { v.pop_back(); });
+
+    // Do some action; Use the failure argument to pretend
+    // if it failed or succeeded.
+
+    // if there was no failure, dismiss the undo guard action.
+    if (!failure) {
+      guard.dismiss();
+    }
+  } // all stack allocated in the mini-scope will be destroyed here.
+
+  if (failure) {
+    EXPECT_EQ(0, v.size()); // the action failed => undo insertion
+  } else {
+    EXPECT_EQ(1, v.size()); // the action succeeded => keep insertion
+  }
+}
+
+TEST(ScopeGuard, UndoAction) {
+  testUndoAction(true);
+  testUndoAction(false);
+}
+
+/**
+ * Sometimes in a try catch block we want to execute a piece of code
+ * regardless if an exception happened or not. For example, you want
+ * to close a db connection regardless if an exception was thrown during
+ * insertion. In Java and other languages there is a finally clause that
+ * helps accomplish this:
+ *
+ *   try {
+ *     dbConn.doInsert(sql);
+ *   } catch (const DbException& dbe) {
+ *     dbConn.recordFailure(dbe);
+ *   } catch (const CriticalException& e) {
+ *     throw e; // re-throw the exception
+ *   } finally {
+ *     dbConn.closeConnection(); // executes no matter what!
+ *   }
+ *
+ * We can approximate this behavior in C++ with ScopeGuard.
+ */
+enum class ErrorBehavior {
+  SUCCESS,
+  HANDLED_ERROR,
+  UNHANDLED_ERROR,
+};
+
+void testFinally(ErrorBehavior error) {
+  bool cleanupOccurred = false;
+
+  try {
+    ScopeGuard guard = makeGuard([&] { cleanupOccurred = true; });
+
+    try {
+      if (error == ErrorBehavior::HANDLED_ERROR) {
+        throw std::runtime_error("throwing an expected error");
+      } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
+        throw "never throw raw strings";
+      }
+    } catch (const std::runtime_error&) {
+    }
+  } catch (...) {
+    // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
+  }
+
+  EXPECT_TRUE(cleanupOccurred);
+}
+
+TEST(ScopeGuard, TryCatchFinally) {
+  testFinally(ErrorBehavior::SUCCESS);
+  testFinally(ErrorBehavior::HANDLED_ERROR);
+  testFinally(ErrorBehavior::UNHANDLED_ERROR);
+}
+
+TEST(ScopeGuard, TEST_SCOPE_EXIT) {
+  int x = 0;
+  {
+    SCOPE_EXIT { ++x; };
+    EXPECT_EQ(0, x);
+  }
+  EXPECT_EQ(1, x);
+}
+
+class Foo {
+public:
+  Foo() {}
+  ~Foo() {
+    try {
+      auto e = std::current_exception();
+      int test = 0;
+      {
+        SCOPE_EXIT { ++test; };
+        EXPECT_EQ(0, test);
+      }
+      EXPECT_EQ(1, test);
+    } catch (const std::exception& ex) {
+      LOG(FATAL) << "Unexpected exception: " << ex.what();
+    }
+  }
+};
+
+TEST(ScopeGuard, TEST_SCOPE_FAILURE2) {
+  try {
+    Foo f;
+    throw std::runtime_error("test");
+  } catch (...) {
+  }
+}
+
+void testScopeFailAndScopeSuccess(ErrorBehavior error, bool expectFail) {
+  bool scopeFailExecuted = false;
+  bool scopeSuccessExecuted = false;
+
+  try {
+    SCOPE_FAIL { scopeFailExecuted = true; };
+    SCOPE_SUCCESS { scopeSuccessExecuted = true; };
+
+    try {
+      if (error == ErrorBehavior::HANDLED_ERROR) {
+        throw std::runtime_error("throwing an expected error");
+      } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
+        throw "never throw raw strings";
+      }
+    } catch (const std::runtime_error&) {
+    }
+  } catch (...) {
+    // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
+  }
+
+  EXPECT_EQ(expectFail, scopeFailExecuted);
+  EXPECT_EQ(!expectFail, scopeSuccessExecuted);
+}
+
+TEST(ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS) {
+  testScopeFailAndScopeSuccess(ErrorBehavior::SUCCESS, false);
+  testScopeFailAndScopeSuccess(ErrorBehavior::HANDLED_ERROR, false);
+  testScopeFailAndScopeSuccess(ErrorBehavior::UNHANDLED_ERROR, true);
+}
+
+TEST(ScopeGuard, TEST_SCOPE_SUCCESS_THROW) {
+  auto lambda = []() {
+    SCOPE_SUCCESS { throw std::runtime_error("ehm"); };
+  };
+  EXPECT_THROW(lambda(), std::runtime_error);
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/SmallLocksTest.cpp b/faux-folly/folly/test/SmallLocksTest.cpp
new file mode 100644
index 0000000..059e41c
--- /dev/null
+++ b/faux-folly/folly/test/SmallLocksTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2015 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/SmallLocks.h>
+#include <cassert>
+#include <cstdio>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using std::string;
+using folly::MicroSpinLock;
+using folly::PicoSpinLock;
+using folly::MSLGuard;
+
+namespace {
+
+struct LockedVal {
+  int ar[1024];
+  MicroSpinLock lock;
+
+  LockedVal() {
+    lock.init();
+    memset(ar, 0, sizeof ar);
+  }
+};
+
+// Compile time test for packed struct support (requires that both of
+// these classes are POD).
+FOLLY_PACK_PUSH
+struct ignore1 { MicroSpinLock msl; int16_t foo; } FOLLY_PACK_ATTR;
+struct ignore2 { PicoSpinLock<uint32_t> psl; int16_t foo; } FOLLY_PACK_ATTR;
+static_assert(sizeof(ignore1) == 3, "Size check failed");
+static_assert(sizeof(ignore2) == 6, "Size check failed");
+static_assert(sizeof(MicroSpinLock) == 1, "Size check failed");
+FOLLY_PACK_POP
+
+LockedVal v;
+void splock_test() {
+
+  const int max = 1000;
+  unsigned int seed = (uintptr_t)pthread_self();
+  for (int i = 0; i < max; i++) {
+    folly::asm_pause();
+    MSLGuard g(v.lock);
+
+    int first = v.ar[0];
+    for (size_t i = 1; i < sizeof v.ar / sizeof i; ++i) {
+      EXPECT_EQ(first, v.ar[i]);
+    }
+
+    int byte = rand_r(&seed);
+    memset(v.ar, char(byte), sizeof v.ar);
+  }
+}
+
+template<class T> struct PslTest {
+  PicoSpinLock<T> lock;
+
+  PslTest() { lock.init(); }
+
+  void doTest() {
+    T ourVal = rand() % (T(1) << (sizeof(T) * 8 - 1));
+    for (int i = 0; i < 10000; ++i) {
+      std::lock_guard<PicoSpinLock<T>> guard(lock);
+      lock.setData(ourVal);
+      for (int n = 0; n < 10; ++n) {
+        folly::asm_volatile_pause();
+        EXPECT_EQ(lock.getData(), ourVal);
+      }
+    }
+  }
+};
+
+template<class T>
+void doPslTest() {
+  PslTest<T> testObj;
+
+  const int nthrs = 17;
+  std::vector<std::thread> threads;
+  for (int i = 0; i < nthrs; ++i) {
+    threads.push_back(std::thread(&PslTest<T>::doTest, &testObj));
+  }
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+struct TestClobber {
+  TestClobber() {
+    lock_.init();
+  }
+
+  void go() {
+    std::lock_guard<MicroSpinLock> g(lock_);
+    // This bug depends on gcc register allocation and is very sensitive. We
+    // have to use DCHECK instead of EXPECT_*.
+    DCHECK(!lock_.try_lock());
+  }
+
+ private:
+  MicroSpinLock lock_;
+};
+
+}
+
+TEST(SmallLocks, SpinLockCorrectness) {
+  EXPECT_EQ(sizeof(MicroSpinLock), 1);
+
+  int nthrs = sysconf(_SC_NPROCESSORS_ONLN) * 2;
+  std::vector<std::thread> threads;
+  for (int i = 0; i < nthrs; ++i) {
+    threads.push_back(std::thread(splock_test));
+  }
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+TEST(SmallLocks, PicoSpinCorrectness) {
+  doPslTest<int16_t>();
+  doPslTest<uint16_t>();
+  doPslTest<int32_t>();
+  doPslTest<uint32_t>();
+  doPslTest<int64_t>();
+  doPslTest<uint64_t>();
+}
+
+TEST(SmallLocks, PicoSpinSigned) {
+  typedef PicoSpinLock<int16_t,0> Lock;
+  Lock val;
+  val.init(-4);
+  EXPECT_EQ(val.getData(), -4);
+
+  {
+    std::lock_guard<Lock> guard(val);
+    EXPECT_EQ(val.getData(), -4);
+    val.setData(-8);
+    EXPECT_EQ(val.getData(), -8);
+  }
+  EXPECT_EQ(val.getData(), -8);
+}
+
+TEST(SmallLocks, RegClobber) {
+  TestClobber().go();
+}
diff --git a/faux-folly/folly/test/SpookyHashV1Test.cpp b/faux-folly/folly/test/SpookyHashV1Test.cpp
new file mode 100644
index 0000000..a9438d6
--- /dev/null
+++ b/faux-folly/folly/test/SpookyHashV1Test.cpp
@@ -0,0 +1,548 @@
+/*
+ * Copyright 2015 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.
+ */
+// SpookyHash: a 128-bit noncryptographic hash function
+// By Bob Jenkins, public domain
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS 1
+#endif
+
+#include <folly/SpookyHashV1.h>
+#include <folly/Benchmark.h>
+
+#include <cinttypes>
+#include <cstdio>
+#include <cstddef>
+#include <cstring>
+#include <cstdlib>
+#include <ctime>
+
+#include <gtest/gtest.h>
+
+using namespace ::folly::hash;
+
+static bool failed = false;
+
+static uint64_t GetTickCount() {
+  timespec ts;
+  clock_gettime(CLOCK_REALTIME, &ts);
+  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;  // milliseconds
+}
+
+class Random
+{
+public:
+    inline uint64_t Value()
+    {
+        uint64_t e = m_a - Rot64(m_b, 23);
+        m_a = m_b ^ Rot64(m_c, 16);
+        m_b = m_c + Rot64(m_d, 11);
+        m_c = m_d + e;
+        m_d = e + m_a;
+        return m_d;
+    }
+
+    inline void Init( uint64_t seed)
+    {
+        m_a = 0xdeadbeef;
+        m_b = m_c = m_d = seed;
+        for (int i=0; i<20; ++i)
+            (void)Value();
+    }
+
+private:
+    static inline uint64_t Rot64(uint64_t x, int k)
+    {
+        return (x << k) | (x >> (64-(k)));
+    }
+
+    uint64_t m_a;
+    uint64_t m_b;
+    uint64_t m_c;
+    uint64_t m_d;
+};
+
+// fastest conceivable hash function (for comparison)
+static void Add(const void *data, size_t length,
+                uint64_t *hash1, uint64_t *hash2)
+{
+    uint64_t *p64 = (uint64_t *)data;
+    uint64_t *end = p64 + length/8;
+    uint64_t hash = *hash1 + *hash2;
+    while (p64 < end)
+    {
+      hash += *p64;
+      ++p64;
+    }
+    *hash1 = hash;
+    *hash2 = hash;
+}
+
+#define BUFSIZE (512)
+void TestResults()
+{
+    printf("\ntesting results ...\n");
+    static const uint32_t expected[BUFSIZE] = {
+        0xa24295ec, 0xfe3a05ce, 0x257fd8ef, 0x3acd5217,
+        0xfdccf85c, 0xc7b5f143, 0x3b0c3ff0, 0x5220f13c,
+        0xa6426724, 0x4d5426b4, 0x43e76b26, 0x051bc437,
+        0xd8f28a02, 0x23ccc30e, 0x811d1a2d, 0x039128d4,
+        0x9cd96a73, 0x216e6a8d, 0x97293fe8, 0xe4fc6d09,
+        0x1ad34423, 0x9722d7e4, 0x5a6fdeca, 0x3c94a7e1,
+        0x81a9a876, 0xae3f7c0e, 0x624b50ee, 0x875e5771,
+        0x0095ab74, 0x1a7333fb, 0x056a4221, 0xa38351fa,
+
+        0x73f575f1, 0x8fded05b, 0x9097138f, 0xbd74620c,
+        0x62d3f5f2, 0x07b78bd0, 0xbafdd81e, 0x0638f2ff,
+        0x1f6e3aeb, 0xa7786473, 0x71700e1d, 0x6b4625ab,
+        0xf02867e1, 0xb2b2408f, 0x9ce21ce5, 0xa62baaaf,
+        0x26720461, 0x434813ee, 0x33bc0f14, 0xaaab098a,
+        0x750af488, 0xc31bf476, 0x9cecbf26, 0x94793cf3,
+        0xe1a27584, 0xe80c4880, 0x1299f748, 0x25e55ed2,
+        0x405e3feb, 0x109e2412, 0x3e55f94f, 0x59575864,
+
+        0x365c869d, 0xc9852e6a, 0x12c30c62, 0x47f5b286,
+        0xb47e488d, 0xa6667571, 0x78220d67, 0xa49e30b9,
+        0x2005ef88, 0xf6d3816d, 0x6926834b, 0xe6116805,
+        0x694777aa, 0x464af25b, 0x0e0e2d27, 0x0ea92eae,
+        0x602c2ca9, 0x1d1d79c5, 0x6364f280, 0x939ee1a4,
+        0x3b851bd8, 0x5bb6f19f, 0x80b9ed54, 0x3496a9f1,
+        0xdf815033, 0x91612339, 0x14c516d6, 0xa3f0a804,
+        0x5e78e975, 0xf408bcd9, 0x63d525ed, 0xa1e459c3,
+
+        0xfde303af, 0x049fc17f, 0xe7ed4489, 0xfaeefdb6,
+        0x2b1b2fa8, 0xc67579a6, 0x5505882e, 0xe3e1c7cb,
+        0xed53bf30, 0x9e628351, 0x8fa12113, 0x7500c30f,
+        0xde1bee00, 0xf1fefe06, 0xdc759c00, 0x4c75e5ab,
+        0xf889b069, 0x695bf8ae, 0x47d6600f, 0xd2a84f87,
+        0xa0ca82a9, 0x8d2b750c, 0xe03d8cd7, 0x581fea33,
+        0x969b0460, 0x36c7b7de, 0x74b3fd20, 0x2bb8bde6,
+        0x13b20dec, 0xa2dcee89, 0xca36229d, 0x06fdb74e,
+
+
+        0x6d9a982d, 0x02503496, 0xbdb4e0d9, 0xbd1f94cf,
+        0x6d26f82d, 0xcf5e41cd, 0x88b67b65, 0x3e1b3ee4,
+        0xb20e5e53, 0x1d9be438, 0xcef9c692, 0x299bd1b2,
+        0xb1279627, 0x210b5f3d, 0x5569bd88, 0x9652ed43,
+        0x7e8e0f8c, 0xdfa01085, 0xcd6d6343, 0xb8739826,
+        0xa52ce9a0, 0xd33ef231, 0x1b4d92c2, 0xabfa116d,
+        0xcdf47800, 0x3a4eefdc, 0xd01f3bcf, 0x30a32f46,
+        0xfb54d851, 0x06a98f67, 0xbdcd0a71, 0x21a00949,
+
+        0xfe7049c9, 0x67ef46d2, 0xa1fabcbc, 0xa4c72db4,
+        0x4a8a910d, 0x85a890ad, 0xc37e9454, 0xfc3d034a,
+        0x6f46cc52, 0x742be7a8, 0xe94ecbc5, 0x5f993659,
+        0x98270309, 0x8d1adae9, 0xea6e035e, 0x293d5fae,
+        0x669955b3, 0x5afe23b5, 0x4c74efbf, 0x98106505,
+        0xfbe09627, 0x3c00e8df, 0x5b03975d, 0x78edc83c,
+        0x117c49c6, 0x66cdfc73, 0xfa55c94f, 0x5bf285fe,
+        0x2db49b7d, 0xfbfeb8f0, 0xb7631bab, 0x837849f3,
+
+        0xf77f3ae5, 0x6e5db9bc, 0xfdd76f15, 0x545abf92,
+        0x8b538102, 0xdd5c9b65, 0xa5adfd55, 0xecbd7bc5,
+        0x9f99ebdd, 0x67500dcb, 0xf5246d1f, 0x2b0c061c,
+        0x927a3747, 0xc77ba267, 0x6da9f855, 0x6240d41a,
+        0xe9d1701d, 0xc69f0c55, 0x2c2c37cf, 0x12d82191,
+        0x47be40d3, 0x165b35cd, 0xb7db42e1, 0x358786e4,
+        0x84b8fc4e, 0x92f57c28, 0xf9c8bbd7, 0xab95a33d,
+        0x11009238, 0xe9770420, 0xd6967e2a, 0x97c1589f,
+
+        0x2ee7e7d3, 0x32cc86da, 0xe47767d1, 0x73e9b61e,
+        0xd35bac45, 0x835a62bb, 0x5d9217b0, 0x43f3f0ed,
+        0x8a97911e, 0x4ec7eb55, 0x4b5a988c, 0xb9056683,
+        0x45456f97, 0x1669fe44, 0xafb861b8, 0x8e83a19c,
+        0x0bab08d6, 0xe6a145a9, 0xc31e5fc2, 0x27621f4c,
+        0x795692fa, 0xb5e33ab9, 0x1bc786b6, 0x45d1c106,
+        0x986531c9, 0x40c9a0ec, 0xff0fdf84, 0xa7359a42,
+        0xfd1c2091, 0xf73463d4, 0x51b0d635, 0x1d602fb4,
+
+
+        0xc56b69b7, 0x6909d3f7, 0xa04d68f4, 0x8d1001a7,
+        0x8ecace50, 0x21ec4765, 0x3530f6b0, 0x645f3644,
+        0x9963ef1e, 0x2b3c70d5, 0xa20c823b, 0x8d26dcae,
+        0x05214e0c, 0x1993896d, 0x62085a35, 0x7b620b67,
+        0x1dd85da2, 0x09ce9b1d, 0xd7873326, 0x063ff730,
+        0xf4ff3c14, 0x09a49d69, 0x532062ba, 0x03ba7729,
+        0xbd9a86cc, 0xe26d02a7, 0x7ccbe5d3, 0x4f662214,
+        0x8b999a66, 0x3d0b92b4, 0x70b210f0, 0xf5b8f16f,
+
+        0x32146d34, 0x430b92bf, 0x8ab6204c, 0x35e6e1ff,
+        0xc2f6c2fa, 0xa2df8a1a, 0x887413ec, 0x7cb7a69f,
+        0x7ac6dbe6, 0x9102d1cb, 0x8892a590, 0xc804fe3a,
+        0xdfc4920a, 0xfc829840, 0x8910d2eb, 0x38a210fd,
+        0x9d840cc9, 0x7b9c827f, 0x3444ca0c, 0x071735ab,
+        0x5e9088e4, 0xc995d60e, 0xbe0bb942, 0x17b089ae,
+        0x050e1054, 0xcf4324f7, 0x1e3e64dd, 0x436414bb,
+        0xc48fc2e3, 0x6b6b83d4, 0x9f6558ac, 0x781b22c5,
+
+        0x7147cfe2, 0x3c221b4d, 0xa5602765, 0x8f01a4f0,
+        0x2a9f14ae, 0x12158cb8, 0x28177c50, 0x1091a165,
+        0x39e4e4be, 0x3e451b7a, 0xd965419c, 0x52053005,
+        0x0798aa53, 0xe6773e13, 0x1207f671, 0xd2ef998b,
+        0xab88a38f, 0xc77a8482, 0xa88fb031, 0x5199e0cd,
+        0x01b30536, 0x46eeb0ef, 0x814259ff, 0x9789a8cf,
+        0x376ec5ac, 0x7087034a, 0x948b6bdd, 0x4281e628,
+        0x2c848370, 0xd76ce66a, 0xe9b6959e, 0x24321a8e,
+
+        0xdeddd622, 0xb890f960, 0xea26c00a, 0x55e7d8b2,
+        0xeab67f09, 0x9227fb08, 0xeebbed06, 0xcac1b0d1,
+        0xb6412083, 0x05d2b0e7, 0x9037624a, 0xc9702198,
+        0x2c8d1a86, 0x3e7d416e, 0xc3f1a39f, 0xf04bdce4,
+        0xc88cdb61, 0xbdc89587, 0x4d29b63b, 0x6f24c267,
+        0x4b529c87, 0x573f5a53, 0xdb3316e9, 0x288eb53b,
+        0xd2c074bd, 0xef44a99a, 0x2b404d2d, 0xf6706464,
+        0xfe824f4c, 0xc3debaf8, 0x12f44f98, 0x03135e76,
+
+
+        0xb4888e7f, 0xb6b2325d, 0x3a138259, 0x513c83ec,
+        0x2386d214, 0x94555500, 0xfbd1522d, 0xda2af018,
+        0x15b054c0, 0x5ad654e6, 0xb6ed00aa, 0xa2f2180e,
+        0x5f662825, 0xecd11366, 0x1de5e99d, 0x07afd2ad,
+        0xcf457b04, 0xe631e10b, 0x83ae8a21, 0x709f0d59,
+        0x3e278bf9, 0x246816db, 0x9f5e8fd3, 0xc5b5b5a2,
+        0xd54a9d5c, 0x4b6f2856, 0x2eb5a666, 0xfc68bdd4,
+        0x1ed1a7f8, 0x98a34b75, 0xc895ada9, 0x2907cc69,
+
+        0x87b0b455, 0xddaf96d9, 0xe7da15a6, 0x9298c82a,
+        0x72bd5cab, 0x2e2a6ad4, 0x7f4b6bb8, 0x525225fe,
+        0x985abe90, 0xac1fd6e1, 0xb8340f23, 0x92985159,
+        0x7d29501d, 0xe75dc744, 0x687501b4, 0x92077dc3,
+        0x58281a67, 0xe7e8e9be, 0xd0e64fd1, 0xb2eb0a30,
+        0x0e1feccd, 0xc0dc4a9e, 0x5c4aeace, 0x2ca5b93c,
+        0xee0ec34f, 0xad78467b, 0x0830e76e, 0x0df63f8b,
+        0x2c2dfd95, 0x9b41ed31, 0x9ff4cddc, 0x1590c412,
+
+        0x2366fc82, 0x7a83294f, 0x9336c4de, 0x2343823c,
+        0x5b681096, 0xf320e4c2, 0xc22b70e2, 0xb5fbfb2a,
+        0x3ebc2fed, 0x11af07bd, 0x429a08c5, 0x42bee387,
+        0x58629e33, 0xfb63b486, 0x52135fbe, 0xf1380e60,
+        0x6355de87, 0x2f0bb19a, 0x167f63ac, 0x507224cf,
+        0xf7c99d00, 0x71646f50, 0x74feb1ca, 0x5f9abfdd,
+        0x278f7d68, 0x70120cd7, 0x4281b0f2, 0xdc8ebe5c,
+        0x36c32163, 0x2da1e884, 0x61877598, 0xbef04402,
+
+        0x304db695, 0xfa8e9add, 0x503bac31, 0x0fe04722,
+        0xf0d59f47, 0xcdc5c595, 0x918c39dd, 0x0cad8d05,
+        0x6b3ed1eb, 0x4d43e089, 0x7ab051f8, 0xdeec371f,
+        0x0f4816ae, 0xf8a1a240, 0xd15317f6, 0xb8efbf0b,
+        0xcdd05df8, 0x4fd5633e, 0x7cf19668, 0x25d8f422,
+        0x72d156f2, 0x2a778502, 0xda7aefb9, 0x4f4f66e8,
+        0x19db6bff, 0x74e468da, 0xa754f358, 0x7339ec50,
+        0x139006f6, 0xefbd0b91, 0x217e9a73, 0x939bd79c
+    };
+
+    uint8_t buf[BUFSIZE];
+    uint32_t saw[BUFSIZE];
+    for (int i=0; i<BUFSIZE; ++i)
+    {
+        buf[i] = i+128;
+        saw[i] = SpookyHashV1::Hash32(buf, i, 0);
+        if (saw[i] != expected[i])
+        {
+            printf("%d: saw 0x%.8x, expected 0x%.8x\n", i, saw[i], expected[i]);
+            failed = true;
+        }
+    }
+}
+#undef BUFSIZE
+
+
+#define NUMBUF (1<<8)
+#define BUFSIZE (1<<21)
+void DoTimingBig(int seed)
+{
+    printf("\ntesting time to hash 512M bytes ...\n");
+
+    char *buf[NUMBUF];
+    for (int i=0; i<NUMBUF; ++i)
+    {
+        buf[i] = (char *)malloc(BUFSIZE);
+        memset(buf[i], (char)seed, BUFSIZE);
+    }
+
+    uint64_t a = GetTickCount();
+    uint64_t hash1 = seed;
+    uint64_t hash2 = seed;
+    for (uint64_t i=0; i<NUMBUF; ++i)
+    {
+        SpookyHashV1::Hash128(buf[i], BUFSIZE, &hash1, &hash2);
+    }
+    uint64_t z = GetTickCount();
+    printf("SpookyHashV1::Hash128, uncached: time is "
+           "%4" PRIu64 " milliseconds\n", z-a);
+
+    a = GetTickCount();
+    for (uint64_t i=0; i<NUMBUF; ++i)
+    {
+        Add(buf[i], BUFSIZE, &hash1, &hash2);
+    }
+    z = GetTickCount();
+    printf("Addition           , uncached: time is "
+           "%4" PRIu64 " milliseconds\n", z-a);
+
+    a = GetTickCount();
+    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)
+    {
+        SpookyHashV1::Hash128(buf[0], 1024, &hash1, &hash2);
+    }
+    z = GetTickCount();
+    printf("SpookyHashV1::Hash128,   cached: time is "
+           "%4" PRIu64 " milliseconds\n", z-a);
+
+    a = GetTickCount();
+    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)
+    {
+        Add(buf[0], 1024, &hash1, &hash2);
+    }
+    z = GetTickCount();
+    printf("Addition           ,   cached: time is "
+           "%4" PRIu64 " milliseconds\n", z-a);
+
+    for (int i=0; i<NUMBUF; ++i)
+    {
+        free(buf[i]);
+        buf[i] = 0;
+    }
+}
+#undef NUMBUF
+#undef BUFSIZE
+
+
+#define BUFSIZE (1<<14)
+#define NUMITER 10000000
+void DoTimingSmall(int seed)
+{
+    printf("\ntesting timing of hashing up to %d cached aligned bytes %d "
+           "times ...\n", BUFSIZE, NUMITER);
+
+    uint64_t buf[BUFSIZE/8];
+    for (int i=0; i<BUFSIZE/8; ++i)
+    {
+        buf[i] = i+seed;
+    }
+
+    for (int i=1; i <= BUFSIZE; i <<= 1)
+    {
+        uint64_t a = GetTickCount();
+        uint64_t hash1 = seed;
+        uint64_t hash2 = seed+i;
+        for (int j=0; j<NUMITER; ++j)
+        {
+            SpookyHashV1::Hash128((char *)buf, i, &hash1, &hash2);
+        }
+        uint64_t z = GetTickCount();
+        printf("%d bytes: hash is %.16" PRIx64 " %.16" PRIx64 ", "
+               "time is %" PRIu64 "\n", i, hash1, hash2, z-a);
+    }
+}
+#undef BUFSIZE
+
+#define BUFSIZE 1024
+void TestAlignment()
+{
+    printf("\ntesting alignment ...\n");
+
+    char buf[BUFSIZE];
+    uint64_t hash[8];
+    for (int i=0; i<BUFSIZE-16; ++i)
+    {
+        for (int j=0; j<8; ++j)
+        {
+            buf[j] = (char)i+j;
+            for (int k=1; k<=i; ++k)
+            {
+                buf[j+k] = k;
+            }
+            buf[j+i+1] = (char)i+j;
+            hash[j] = SpookyHashV1::Hash64((const void *)(buf+j+1), i, 0);
+        }
+        for (int j=1; j<8; ++j)
+        {
+            if (hash[0] != hash[j])
+            {
+                printf("alignment problems: %d %d\n", i, j);
+                failed = true;
+            }
+        }
+    }
+}
+#undef BUFSIZE
+
+// test that all deltas of one or two input bits affect all output bits
+#define BUFSIZE 256
+#define TRIES 50
+#define MEASURES 6
+void TestDeltas(int seed)
+{
+    printf("\nall 1 or 2 bit input deltas get %d tries to flip every output "
+           "bit ...\n", TRIES);
+
+    Random random;
+    random.Init((uint64_t)seed);
+
+    // for messages 0..BUFSIZE-1 bytes
+    for (int h=0; h<BUFSIZE; ++h)
+    {
+        int maxk = 0;
+        // first bit to set
+        for (int i=0; i<h*8; ++i)
+        {
+            // second bit to set, or don't have a second bit
+            for (int j=0; j<=i; ++j)
+            {
+                uint64_t measure[MEASURES][2];
+                uint64_t counter[MEASURES][2];
+                for (int l=0; l<2; ++l)
+                {
+                    for (int m=0; m<MEASURES; ++m)
+                    {
+                        counter[m][l] = 0;
+                    }
+                }
+
+                // try to hit every output bit TRIES times
+                int k;
+                for (k=0; k<TRIES; ++k)
+                {
+                    uint8_t buf1[BUFSIZE];
+                    uint8_t buf2[BUFSIZE];
+                    int done = 1;
+                    for (int l=0; l<h; ++l)
+                    {
+                        buf1[l] = buf2[l] = random.Value();
+                    }
+                    buf1[i/8] ^= (1 << (i%8));
+                    if (j != i)
+                    {
+                        buf1[j/8] ^= (1 << (j%8));
+                    }
+                    SpookyHashV1::Hash128(buf1, h,
+                            &measure[0][0], &measure[0][1]);
+                    SpookyHashV1::Hash128(buf2, h,
+                            &measure[1][0], &measure[1][1]);
+                    for (int l=0; l<2; ++l) {
+                        measure[2][l] = measure[0][l] ^ measure[1][l];
+                        measure[3][l] = ~(measure[0][l] ^ measure[1][l]);
+                        measure[4][l] = measure[0][l] - measure[1][l];
+                        measure[4][l] ^= (measure[4][l]>>1);
+                        measure[5][l] = measure[0][l] + measure[1][l];
+                        measure[5][l] ^= (measure[4][l]>>1);
+                    }
+                    for (int l=0; l<2; ++l)
+                    {
+                        for (int m=0; m<MEASURES; ++m)
+                        {
+                            counter[m][l] |= measure[m][l];
+                            if (~counter[m][l]) done = 0;
+                        }
+                    }
+                    if (done) break;
+                }
+                if (k == TRIES)
+                {
+                    printf("failed %d %d %d\n", h, i, j);
+                    failed = true;
+                }
+                else if (k > maxk)
+                {
+                    maxk = k;
+                }
+            }
+        }
+        printf("passed for buffer size %d  max %d\n", h, maxk);
+    }
+}
+#undef BUFSIZE
+#undef TRIES
+#undef MEASURES
+
+
+// test that hashing pieces has the same behavior as hashing the whole
+#define BUFSIZE 1024
+void TestPieces()
+{
+    printf("\ntesting pieces ...\n");
+    char buf[BUFSIZE];
+    for (int i=0; i<BUFSIZE; ++i)
+    {
+        buf[i] = i;
+    }
+    for (int i=0; i<BUFSIZE; ++i)
+    {
+        uint64_t a,b,c,d,seed1=1,seed2=2;
+        SpookyHashV1 state;
+
+        // all as one call
+        a = seed1;
+        b = seed2;
+        SpookyHashV1::Hash128(buf, i, &a, &b);
+
+        // all as one piece
+        c = 0xdeadbeefdeadbeef;
+        d = 0xbaceba11baceba11;
+        state.Init(seed1, seed2);
+        state.Update(buf, i);
+        state.Final(&c, &d);
+
+        if (a != c)
+        {
+            printf("wrong a %d: %.16" PRIx64 " %.16" PRIx64 "\n", i, a,c);
+            failed = true;
+        }
+        if (b != d)
+        {
+            printf("wrong b %d: %.16" PRIx64 " %.16" PRIx64 "\n", i, b,d);
+            failed = true;
+        }
+
+        // all possible two consecutive pieces
+        for (int j=0; j<i; ++j)
+        {
+            c = seed1;
+            d = seed2;
+            state.Init(c, d);
+            state.Update(&buf[0], j);
+            state.Update(&buf[j], i-j);
+            state.Final(&c, &d);
+            if (a != c)
+            {
+                printf("wrong a %d %d: %.16" PRIx64 " %.16" PRIx64 "\n",
+                       j, i, a,c);
+                failed = true;
+            }
+            if (b != d)
+            {
+                printf("wrong b %d %d: %.16" PRIx64 " %.16" PRIx64 "\n",
+                       j, i, b,d);
+                failed = true;
+            }
+        }
+    }
+}
+#undef BUFSIZE
+
+TEST(SpookyHashV1, Main) {
+    TestResults();
+    TestAlignment();
+    TestPieces();
+    DoTimingBig(1);
+    // tudorb@fb.com: Commented out slow tests
+#if 0
+    DoTimingSmall(argc);
+    TestDeltas(argc);
+#endif
+    CHECK_EQ(failed, 0);
+}
diff --git a/faux-folly/folly/test/SpookyHashV2Test.cpp b/faux-folly/folly/test/SpookyHashV2Test.cpp
new file mode 100644
index 0000000..0491fc1
--- /dev/null
+++ b/faux-folly/folly/test/SpookyHashV2Test.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright 2015 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.
+ */
+// SpookyHash: a 128-bit noncryptographic hash function
+// By Bob Jenkins, public domain
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS 1
+#endif
+
+#include <folly/SpookyHashV2.h>
+#include <folly/Benchmark.h>
+
+#include <cinttypes>
+#include <cstdio>
+#include <cstddef>
+#include <cstring>
+#include <cstdlib>
+#include <ctime>
+
+#include <gtest/gtest.h>
+
+using namespace ::folly::hash;
+
+static bool failed = false;
+
+static uint64_t GetTickCount() {
+  timespec ts;
+  clock_gettime(CLOCK_REALTIME, &ts);
+  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;  // milliseconds
+}
+
+class Random
+{
+public:
+    inline uint64_t Value()
+    {
+        uint64_t e = m_a - Rot64(m_b, 23);
+        m_a = m_b ^ Rot64(m_c, 16);
+        m_b = m_c + Rot64(m_d, 11);
+        m_c = m_d + e;
+        m_d = e + m_a;
+        return m_d;
+    }
+
+    inline void Init( uint64_t seed)
+    {
+        m_a = 0xdeadbeef;
+        m_b = m_c = m_d = seed;
+        for (int i=0; i<20; ++i)
+            (void)Value();
+    }
+
+private:
+    static inline uint64_t Rot64(uint64_t x, int k)
+    {
+        return (x << k) | (x >> (64-(k)));
+    }
+
+    uint64_t m_a;
+    uint64_t m_b;
+    uint64_t m_c;
+    uint64_t m_d;
+};
+
+// fastest conceivable hash function (for comparison)
+static void Add(const void *data, size_t length,
+                uint64_t *hash1, uint64_t *hash2)
+{
+    uint64_t *p64 = (uint64_t *)data;
+    uint64_t *end = p64 + length/8;
+    uint64_t hash = *hash1 + *hash2;
+    while (p64 < end)
+    {
+      hash += *p64;
+      ++p64;
+    }
+    *hash1 = hash;
+    *hash2 = hash;
+}
+
+#define BUFSIZE (512)
+void TestResults()
+{
+    printf("\ntesting results ...\n");
+    static const uint64_t expected[BUFSIZE] = {
+      0x6bf50919,0x70de1d26,0xa2b37298,0x35bc5fbf,
+      0x8223b279,0x5bcb315e,0x53fe88a1,0xf9f1a233,
+      0xee193982,0x54f86f29,0xc8772d36,0x9ed60886,
+      0x5f23d1da,0x1ed9f474,0xf2ef0c89,0x83ec01f9,
+      0xf274736c,0x7e9ac0df,0xc7aed250,0xb1015811,
+      0xe23470f5,0x48ac20c4,0xe2ab3cd5,0x608f8363,
+      0xd0639e68,0xc4e8e7ab,0x863c7c5b,0x4ea63579,
+      0x99ae8622,0x170c658b,0x149ba493,0x027bca7c,
+      0xe5cfc8b6,0xce01d9d7,0x11103330,0x5d1f5ed4,
+      0xca720ecb,0xef408aec,0x733b90ec,0x855737a6,
+      0x9856c65f,0x647411f7,0x50777c74,0xf0f1a8b7,
+      0x9d7e55a5,0xc68dd371,0xfc1af2cc,0x75728d0a,
+      0x390e5fdc,0xf389b84c,0xfb0ccf23,0xc95bad0e,
+      0x5b1cb85a,0x6bdae14f,0x6deb4626,0x93047034,
+      0x6f3266c6,0xf529c3bd,0x396322e7,0x3777d042,
+      0x1cd6a5a2,0x197b402e,0xc28d0d2b,0x09c1afb4,
+
+      0x069c8bb7,0x6f9d4e1e,0xd2621b5c,0xea68108d,
+      0x8660cb8f,0xd61e6de6,0x7fba15c7,0xaacfaa97,
+      0xdb381902,0x4ea22649,0x5d414a1e,0xc3fc5984,
+      0xa0fc9e10,0x347dc51c,0x37545fb6,0x8c84b26b,
+      0xf57efa5d,0x56afaf16,0xb6e1eb94,0x9218536a,
+      0xe3cc4967,0xd3275ef4,0xea63536e,0x6086e499,
+      0xaccadce7,0xb0290d82,0x4ebfd0d6,0x46ccc185,
+      0x2eeb10d3,0x474e3c8c,0x23c84aee,0x3abae1cb,
+      0x1499b81a,0xa2993951,0xeed176ad,0xdfcfe84c,
+      0xde4a961f,0x4af13fe6,0xe0069c42,0xc14de8f5,
+      0x6e02ce8f,0x90d19f7f,0xbca4a484,0xd4efdd63,
+      0x780fd504,0xe80310e3,0x03abbc12,0x90023849,
+      0xd6f6fb84,0xd6b354c5,0x5b8575f0,0x758f14e4,
+      0x450de862,0x90704afb,0x47209a33,0xf226b726,
+      0xf858dab8,0x7c0d6de9,0xb05ce777,0xee5ff2d4,
+      0x7acb6d5c,0x2d663f85,0x41c72a91,0x82356bf2,
+
+      0x94e948ec,0xd358d448,0xeca7814d,0x78cd7950,
+      0xd6097277,0x97782a5d,0xf43fc6f4,0x105f0a38,
+      0x9e170082,0x4bfe566b,0x4371d25f,0xef25a364,
+      0x698eb672,0x74f850e4,0x4678ff99,0x4a290dc6,
+      0x3918f07c,0x32c7d9cd,0x9f28e0af,0x0d3c5a86,
+      0x7bfc8a45,0xddf0c7e1,0xdeacb86b,0x970b3c5c,
+      0x5e29e199,0xea28346d,0x6b59e71b,0xf8a8a46a,
+      0x862f6ce4,0x3ccb740b,0x08761e9e,0xbfa01e5f,
+      0xf17cfa14,0x2dbf99fb,0x7a0be420,0x06137517,
+      0xe020b266,0xd25bfc61,0xff10ed00,0x42e6be8b,
+      0x029ef587,0x683b26e0,0xb08afc70,0x7c1fd59e,
+      0xbaae9a70,0x98c8c801,0xb6e35a26,0x57083971,
+      0x90a6a680,0x1b44169e,0x1dce237c,0x518e0a59,
+      0xccb11358,0x7b8175fb,0xb8fe701a,0x10d259bb,
+      0xe806ce10,0x9212be79,0x4604ae7b,0x7fa22a84,
+      0xe715b13a,0x0394c3b2,0x11efbbae,0xe13d9e19,
+
+      0x77e012bd,0x2d05114c,0xaecf2ddd,0xb2a2b4aa,
+      0xb9429546,0x55dce815,0xc89138f8,0x46dcae20,
+      0x1f6f7162,0x0c557ebc,0x5b996932,0xafbbe7e2,
+      0xd2bd5f62,0xff475b9f,0x9cec7108,0xeaddcffb,
+      0x5d751aef,0xf68f7bdf,0xf3f4e246,0x00983fcd,
+      0x00bc82bb,0xbf5fd3e7,0xe80c7e2c,0x187d8b1f,
+      0xefafb9a7,0x8f27a148,0x5c9606a9,0xf2d2be3e,
+      0xe992d13a,0xe4bcd152,0xce40b436,0x63d6a1fc,
+      0xdc1455c4,0x64641e39,0xd83010c9,0x2d535ae0,
+      0x5b748f3e,0xf9a9146b,0x80f10294,0x2859acd4,
+      0x5fc846da,0x56d190e9,0x82167225,0x98e4daba,
+      0xbf7865f3,0x00da7ae4,0x9b7cd126,0x644172f8,
+      0xde40c78f,0xe8803efc,0xdd331a2b,0x48485c3c,
+      0x4ed01ddc,0x9c0b2d9e,0xb1c6e9d7,0xd797d43c,
+      0x274101ff,0x3bf7e127,0x91ebbc56,0x7ffeb321,
+      0x4d42096f,0xd6e9456a,0x0bade318,0x2f40ee0b,
+
+      0x38cebf03,0x0cbc2e72,0xbf03e704,0x7b3e7a9a,
+      0x8e985acd,0x90917617,0x413895f8,0xf11dde04,
+      0xc66f8244,0xe5648174,0x6c420271,0x2469d463,
+      0x2540b033,0xdc788e7b,0xe4140ded,0x0990630a,
+      0xa54abed4,0x6e124829,0xd940155a,0x1c8836f6,
+      0x38fda06c,0x5207ab69,0xf8be9342,0x774882a8,
+      0x56fc0d7e,0x53a99d6e,0x8241f634,0x9490954d,
+      0x447130aa,0x8cc4a81f,0x0868ec83,0xc22c642d,
+      0x47880140,0xfbff3bec,0x0f531f41,0xf845a667,
+      0x08c15fb7,0x1996cd81,0x86579103,0xe21dd863,
+      0x513d7f97,0x3984a1f1,0xdfcdc5f4,0x97766a5e,
+      0x37e2b1da,0x41441f3f,0xabd9ddba,0x23b755a9,
+      0xda937945,0x103e650e,0x3eef7c8f,0x2760ff8d,
+      0x2493a4cd,0x1d671225,0x3bf4bd4c,0xed6e1728,
+      0xc70e9e30,0x4e05e529,0x928d5aa6,0x164d0220,
+      0xb5184306,0x4bd7efb3,0x63830f11,0xf3a1526c,
+
+      0xf1545450,0xd41d5df5,0x25a5060d,0x77b368da,
+      0x4fe33c7e,0xeae09021,0xfdb053c4,0x2930f18d,
+      0xd37109ff,0x8511a781,0xc7e7cdd7,0x6aeabc45,
+      0xebbeaeaa,0x9a0c4f11,0xda252cbb,0x5b248f41,
+      0x5223b5eb,0xe32ab782,0x8e6a1c97,0x11d3f454,
+      0x3e05bd16,0x0059001d,0xce13ac97,0xf83b2b4c,
+      0x71db5c9a,0xdc8655a6,0x9e98597b,0x3fcae0a2,
+      0x75e63ccd,0x076c72df,0x4754c6ad,0x26b5627b,
+      0xd818c697,0x998d5f3d,0xe94fc7b2,0x1f49ad1a,
+      0xca7ff4ea,0x9fe72c05,0xfbd0cbbf,0xb0388ceb,
+      0xb76031e3,0xd0f53973,0xfb17907c,0xa4c4c10f,
+      0x9f2d8af9,0xca0e56b0,0xb0d9b689,0xfcbf37a3,
+      0xfede8f7d,0xf836511c,0x744003fc,0x89eba576,
+      0xcfdcf6a6,0xc2007f52,0xaaaf683f,0x62d2f9ca,
+      0xc996f77f,0x77a7b5b3,0x8ba7d0a4,0xef6a0819,
+      0xa0d903c0,0x01b27431,0x58fffd4c,0x4827f45c,
+
+      0x44eb5634,0xae70edfc,0x591c740b,0x478bf338,
+      0x2f3b513b,0x67bf518e,0x6fef4a0c,0x1e0b6917,
+      0x5ac0edc5,0x2e328498,0x077de7d5,0x5726020b,
+      0x2aeda888,0x45b637ca,0xcf60858d,0x3dc91ae2,
+      0x3e6d5294,0xe6900d39,0x0f634c71,0x827a5fa4,
+      0xc713994b,0x1c363494,0x3d43b615,0xe5fe7d15,
+      0xf6ada4f2,0x472099d5,0x04360d39,0x7f2a71d0,
+      0x88a4f5ff,0x2c28fac5,0x4cd64801,0xfd78dd33,
+      0xc9bdd233,0x21e266cc,0x9bbf419d,0xcbf7d81d,
+      0x80f15f96,0x04242657,0x53fb0f66,0xded11e46,
+      0xf2fdba97,0x8d45c9f1,0x4eeae802,0x17003659,
+      0xb9db81a7,0xe734b1b2,0x9503c54e,0xb7c77c3e,
+      0x271dd0ab,0xd8b906b5,0x0d540ec6,0xf03b86e0,
+      0x0fdb7d18,0x95e261af,0xad9ec04e,0x381f4a64,
+      0xfec798d7,0x09ea20be,0x0ef4ca57,0x1e6195bb,
+      0xfd0da78b,0xcea1653b,0x157d9777,0xf04af50f,
+
+      0xad7baa23,0xd181714a,0x9bbdab78,0x6c7d1577,
+      0x645eb1e7,0xa0648264,0x35839ca6,0x2287ef45,
+      0x32a64ca3,0x26111f6f,0x64814946,0xb0cddaf1,
+      0x4351c59e,0x1b30471c,0xb970788a,0x30e9f597,
+      0xd7e58df1,0xc6d2b953,0xf5f37cf4,0x3d7c419e,
+      0xf91ecb2d,0x9c87fd5d,0xb22384ce,0x8c7ac51c,
+      0x62c96801,0x57e54091,0x964536fe,0x13d3b189,
+      0x4afd1580,0xeba62239,0xb82ea667,0xae18d43a,
+      0xbef04402,0x1942534f,0xc54bf260,0x3c8267f5,
+      0xa1020ddd,0x112fcc8a,0xde596266,0xe91d0856,
+      0xf300c914,0xed84478e,0x5b65009e,0x4764da16,
+      0xaf8e07a2,0x4088dc2c,0x9a0cad41,0x2c3f179b,
+      0xa67b83f7,0xf27eab09,0xdbe10e28,0xf04c911f,
+      0xd1169f87,0x8e1e4976,0x17f57744,0xe4f5a33f,
+      0x27c2e04b,0x0b7523bd,0x07305776,0xc6be7503,
+      0x918fa7c9,0xaf2e2cd9,0x82046f8e,0xcc1c8250
+    };
+
+    uint8_t buf[BUFSIZE];
+    uint32_t saw[BUFSIZE];
+    for (int i=0; i<BUFSIZE; ++i)
+    {
+        buf[i] = i+128;
+        saw[i] = SpookyHashV2::Hash32(buf, i, 0);
+        if (saw[i] != expected[i])
+        {
+            printf("%3d: saw 0x%.8x, expected 0x%.8" PRIx64 "\n", i, saw[i],
+                   expected[i]);
+            failed = true;
+        }
+    }
+}
+#undef BUFSIZE
+
+
+#define NUMBUF (1<<8)
+#define BUFSIZE (1<<21)
+void DoTimingBig(int seed)
+{
+    printf("\ntesting time to hash 512M bytes ...\n");
+
+    char *buf[NUMBUF];
+    for (int i=0; i<NUMBUF; ++i)
+    {
+        buf[i] = (char *)malloc(BUFSIZE);
+        memset(buf[i], (char)seed, BUFSIZE);
+    }
+
+    uint64_t a = GetTickCount();
+    uint64_t hash1 = seed;
+    uint64_t hash2 = seed;
+    for (uint64_t i=0; i<NUMBUF; ++i)
+    {
+        SpookyHashV2::Hash128(buf[i], BUFSIZE, &hash1, &hash2);
+    }
+    uint64_t z = GetTickCount();
+    printf("SpookyHashV2::Hash128, uncached: time is "
+           "%4" PRId64 " milliseconds\n", z-a);
+
+    a = GetTickCount();
+    for (uint64_t i=0; i<NUMBUF; ++i)
+    {
+        Add(buf[i], BUFSIZE, &hash1, &hash2);
+    }
+    z = GetTickCount();
+    printf("Addition           , uncached: time is %4" PRId64 " milliseconds\n",
+           z-a);
+
+    a = GetTickCount();
+    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)
+    {
+        SpookyHashV2::Hash128(buf[0], 1024, &hash1, &hash2);
+    }
+    z = GetTickCount();
+    printf("SpookyHashV2::Hash128,   cached: time is "
+           "%4" PRId64 " milliseconds\n", z-a);
+
+    a = GetTickCount();
+    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)
+    {
+        Add(buf[0], 1024, &hash1, &hash2);
+    }
+    z = GetTickCount();
+    printf("Addition           ,   cached: time is %4" PRId64 " milliseconds\n",
+           z-a);
+
+    for (int i=0; i<NUMBUF; ++i)
+    {
+        free(buf[i]);
+        buf[i] = 0;
+    }
+}
+#undef NUMBUF
+#undef BUFSIZE
+
+
+#define BUFSIZE (1<<14)
+#define NUMITER 10000000
+void DoTimingSmall(int seed)
+{
+    printf("\ntesting timing of hashing up to %d cached aligned bytes %d "
+           "times ...\n", BUFSIZE, NUMITER);
+
+    uint64_t buf[BUFSIZE/8];
+    for (int i=0; i<BUFSIZE/8; ++i)
+    {
+        buf[i] = i+seed;
+    }
+
+    for (int i=1; i <= BUFSIZE; i <<= 1)
+    {
+        uint64_t a = GetTickCount();
+        uint64_t hash1 = seed;
+        uint64_t hash2 = seed+i;
+        for (int j=0; j<NUMITER; ++j)
+        {
+            SpookyHashV2::Hash128((char *)buf, i, &hash1, &hash2);
+        }
+        uint64_t z = GetTickCount();
+        printf("%d bytes: hash is %.16" PRIx64 " %.16" PRIx64 ", "
+               "time is %" PRId64 "\n", i, hash1, hash2, z-a);
+    }
+}
+#undef BUFSIZE
+
+#define BUFSIZE 1024
+void TestAlignment()
+{
+    printf("\ntesting alignment ...\n");
+
+    char buf[BUFSIZE];
+    uint64_t hash[8];
+    for (int i=0; i<BUFSIZE-16; ++i)
+    {
+        for (int j=0; j<8; ++j)
+        {
+            buf[j] = (char)i+j;
+            for (int k=1; k<=i; ++k)
+            {
+                buf[j+k] = k;
+            }
+            buf[j+i+1] = (char)i+j;
+            hash[j] = SpookyHashV2::Hash64((const void *)(buf+j+1), i, 0);
+        }
+        for (int j=1; j<8; ++j)
+        {
+            if (hash[0] != hash[j])
+            {
+                printf("alignment problems: %d %d\n", i, j);
+                failed = true;
+            }
+        }
+    }
+}
+#undef BUFSIZE
+
+// test that all deltas of one or two input bits affect all output bits
+#define BUFSIZE 256
+#define TRIES 50
+#define MEASURES 6
+void TestDeltas(int seed)
+{
+    printf("\nall 1 or 2 bit input deltas get %d tries to flip every output "
+           "bit ...\n", TRIES);
+
+    Random random;
+    random.Init((uint64_t)seed);
+
+    // for messages 0..BUFSIZE-1 bytes
+    for (int h=0; h<BUFSIZE; ++h)
+    {
+        int maxk = 0;
+        // first bit to set
+        for (int i=0; i<h*8; ++i)
+        {
+            // second bit to set, or don't have a second bit
+            for (int j=0; j<=i; ++j)
+            {
+                uint64_t measure[MEASURES][2];
+                uint64_t counter[MEASURES][2];
+                for (int l=0; l<2; ++l)
+                {
+                    for (int m=0; m<MEASURES; ++m)
+                    {
+                        counter[m][l] = 0;
+                    }
+                }
+
+                // try to hit every output bit TRIES times
+                int k;
+                for (k=0; k<TRIES; ++k)
+                {
+                    uint8_t buf1[BUFSIZE];
+                    uint8_t buf2[BUFSIZE];
+                    int done = 1;
+                    for (int l=0; l<h; ++l)
+                    {
+                        buf1[l] = buf2[l] = random.Value();
+                    }
+                    buf1[i/8] ^= (1 << (i%8));
+                    if (j != i)
+                    {
+                        buf1[j/8] ^= (1 << (j%8));
+                    }
+                    SpookyHashV2::Hash128(buf1, h,
+                            &measure[0][0], &measure[0][1]);
+                    SpookyHashV2::Hash128(buf2, h,
+                            &measure[1][0], &measure[1][1]);
+                    for (int l=0; l<2; ++l) {
+                        measure[2][l] = measure[0][l] ^ measure[1][l];
+                        measure[3][l] = ~(measure[0][l] ^ measure[1][l]);
+                        measure[4][l] = measure[0][l] - measure[1][l];
+                        measure[4][l] ^= (measure[4][l]>>1);
+                        measure[5][l] = measure[0][l] + measure[1][l];
+                        measure[5][l] ^= (measure[4][l]>>1);
+                    }
+                    for (int l=0; l<2; ++l)
+                    {
+                        for (int m=0; m<MEASURES; ++m)
+                        {
+                            counter[m][l] |= measure[m][l];
+                            if (~counter[m][l]) done = 0;
+                        }
+                    }
+                    if (done) break;
+                }
+                if (k == TRIES)
+                {
+                    printf("failed %d %d %d\n", h, i, j);
+                    failed = true;
+                }
+                else if (k > maxk)
+                {
+                    maxk = k;
+                }
+            }
+        }
+        printf("passed for buffer size %d  max %d\n", h, maxk);
+    }
+}
+#undef BUFSIZE
+#undef TRIES
+#undef MEASURES
+
+
+// test that hashing pieces has the same behavior as hashing the whole
+#define BUFSIZE 1024
+void TestPieces()
+{
+    printf("\ntesting pieces ...\n");
+    char buf[BUFSIZE];
+    for (int i=0; i<BUFSIZE; ++i)
+    {
+        buf[i] = i;
+    }
+    for (int i=0; i<BUFSIZE; ++i)
+    {
+        uint64_t a,b,c,d,seed1=1,seed2=2;
+        SpookyHashV2 state;
+
+        // all as one call
+        a = seed1;
+        b = seed2;
+        SpookyHashV2::Hash128(buf, i, &a, &b);
+
+        // all as one piece
+        c = 0xdeadbeefdeadbeef;
+        d = 0xbaceba11baceba11;
+        state.Init(seed1, seed2);
+        state.Update(buf, i);
+        state.Final(&c, &d);
+
+        if (a != c)
+        {
+            printf("wrong a %d: %.16" PRIx64 " %.16" PRIx64 "\n", i, a,c);
+            failed = true;
+        }
+        if (b != d)
+        {
+            printf("wrong b %d: %.16" PRIx64 " %.16" PRIx64 "\n", i, b,d);
+            failed = true;
+        }
+
+        // all possible two consecutive pieces
+        for (int j=0; j<i; ++j)
+        {
+            c = seed1;
+            d = seed2;
+            state.Init(c, d);
+            state.Update(&buf[0], j);
+            state.Update(&buf[j], i-j);
+            state.Final(&c, &d);
+            if (a != c)
+            {
+                printf("wrong a %d %d: %.16" PRIx64 " %.16" PRIx64 "\n",
+                       j, i, a,c);
+                failed = true;
+            }
+            if (b != d)
+            {
+                printf("wrong b %d %d: %.16" PRIx64 " %.16" PRIx64 "\n",
+                       j, i, b,d);
+                failed = true;
+            }
+        }
+    }
+}
+#undef BUFSIZE
+
+TEST(SpookyHashV2, Main) {
+    TestResults();
+    TestAlignment();
+    TestPieces();
+    DoTimingBig(1);
+    // tudorb@fb.com: Commented out slow tests
+#if 0
+    DoTimingSmall(argc);
+    TestDeltas(argc);
+#endif
+    CHECK_EQ(failed, 0);
+}
diff --git a/faux-folly/folly/test/StringBenchmark.cpp b/faux-folly/folly/test/StringBenchmark.cpp
new file mode 100644
index 0000000..c9c90d7
--- /dev/null
+++ b/faux-folly/folly/test/StringBenchmark.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 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/Benchmark.h>
+#include <folly/String.h>
+
+BENCHMARK(libc_tolower, iters) {
+  static const size_t kSize = 256;
+  // This array is static to keep the compiler from optimizing the
+  // entire function down to a no-op if it has an inlined impl of
+  // tolower and thus is able to tell that there are no side-effects.
+  // No side-effects + no writes to anything other than local variables
+  // + no return value = no need to run any of the code in the function.
+  // gcc, for example, makes that optimization with -O2.
+  static char input[kSize];
+  for (size_t i = 0; i < kSize; i++) {
+    input[i] = (char)(i & 0xff);
+  }
+  for (auto i = iters; i > 0; i--) {
+    for (size_t offset = 0; offset < kSize; offset++) {
+      input[offset] = tolower(input[offset]);
+    }
+  }
+}
+
+BENCHMARK(folly_toLowerAscii, iters) {
+  static const size_t kSize = 256;
+  static char input[kSize];
+  for (size_t i = 0; i < kSize; i++) {
+    input[i] = (char)(i & 0xff);
+  }
+  for (auto i = iters; i > 0; i--) {
+    folly::toLowerAscii(input, kSize);
+  }
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return 0;
+}
+
+/*
+Results on x86_64:
+============================================================================
+folly/test/StringBenchmark.cpp                  relative  time/iter  iters/s
+============================================================================
+libc_tolower                                                 1.30us  767.50K
+folly_toLowerAscii                                         115.21ns    8.68M
+============================================================================
+*/
diff --git a/faux-folly/folly/test/StringTest.cpp b/faux-folly/folly/test/StringTest.cpp
new file mode 100644
index 0000000..9091576
--- /dev/null
+++ b/faux-folly/folly/test/StringTest.cpp
@@ -0,0 +1,1364 @@
+/*
+ * Copyright 2015 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/String.h>
+
+#include <string>
+#include <cstdarg>
+#include <random>
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+
+using namespace folly;
+using namespace std;
+
+namespace {
+
+/* For some reason, GCC 4.9 doesn't implicitly cast 'const char[]' to
+ * 'const char*' to folly::StringPiece. This cast helps it along.
+ */
+inline const char* to_ccs(const char ra[])
+{
+    return static_cast<const char*>(ra);
+}
+
+} /* anonymous namespace */
+
+TEST(StringPrintf, BasicTest) {
+  EXPECT_EQ("abc", stringPrintf("%s", "abc"));
+  EXPECT_EQ("abc", stringPrintf("%sbc", "a"));
+  EXPECT_EQ("abc", stringPrintf("a%sc", "b"));
+  EXPECT_EQ("abc", stringPrintf("ab%s", "c"));
+
+  EXPECT_EQ("abc", stringPrintf("abc"));
+}
+
+TEST(StringPrintf, NumericFormats) {
+  EXPECT_EQ("12", stringPrintf("%d", 12));
+  EXPECT_EQ("5000000000", stringPrintf("%ld", 5000000000UL));
+  EXPECT_EQ("5000000000", stringPrintf("%ld", 5000000000L));
+  EXPECT_EQ("-5000000000", stringPrintf("%ld", -5000000000L));
+  EXPECT_EQ("-1", stringPrintf("%d", 0xffffffff));
+  EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffff));
+  EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffffUL));
+
+  EXPECT_EQ("7.7", stringPrintf("%1.1f", 7.7));
+  EXPECT_EQ("7.7", stringPrintf("%1.1lf", 7.7));
+  EXPECT_EQ("7.70000000000000018",
+            stringPrintf("%.17f", 7.7));
+  EXPECT_EQ("7.70000000000000018",
+            stringPrintf("%.17lf", 7.7));
+}
+
+TEST(StringPrintf, Appending) {
+  string s;
+  stringAppendf(&s, "a%s", "b");
+  stringAppendf(&s, "%c", 'c');
+  EXPECT_EQ(s, "abc");
+  stringAppendf(&s, " %d", 123);
+  EXPECT_EQ(s, "abc 123");
+}
+
+void vprintfCheck(const char* expected, const char* fmt, ...) {
+  va_list apOrig;
+  va_start(apOrig, fmt);
+  SCOPE_EXIT {
+    va_end(apOrig);
+  };
+  va_list ap;
+  va_copy(ap, apOrig);
+  SCOPE_EXIT {
+    va_end(ap);
+  };
+
+  // Check both APIs for calling stringVPrintf()
+  EXPECT_EQ(expected, stringVPrintf(fmt, ap));
+  va_end(ap);
+  va_copy(ap, apOrig);
+
+  std::string out;
+  stringVPrintf(&out, fmt, ap);
+  va_end(ap);
+  va_copy(ap, apOrig);
+  EXPECT_EQ(expected, out);
+
+  // Check stringVAppendf() as well
+  std::string prefix = "foobar";
+  out = prefix;
+  EXPECT_EQ(prefix + expected, stringVAppendf(&out, fmt, ap));
+  va_end(ap);
+  va_copy(ap, apOrig);
+}
+
+void vprintfError(const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  SCOPE_EXIT {
+    va_end(ap);
+  };
+
+  // OSX's sprintf family does not return a negative number on a bad format
+  // string, but Linux does. It's unclear to me which behavior is more
+  // correct.
+#ifdef HAVE_VSNPRINTF_ERRORS
+  EXPECT_THROW({stringVPrintf(fmt, ap);},
+               std::runtime_error);
+#endif
+}
+
+TEST(StringPrintf, VPrintf) {
+  vprintfCheck("foo", "%s", "foo");
+  vprintfCheck("long string requiring reallocation 1 2 3 0x12345678",
+               "%s %s %d %d %d %#x",
+               "long string", "requiring reallocation", 1, 2, 3, 0x12345678);
+  vprintfError("bogus%", "foo");
+}
+
+TEST(StringPrintf, VariousSizes) {
+  // Test a wide variety of output sizes, making sure to cross the
+  // vsnprintf buffer boundary implementation detail.
+  for (int i = 0; i < 4096; ++i) {
+    string expected(i + 1, 'a');
+    expected = "X" + expected + "X";
+    string result = stringPrintf("%s", expected.c_str());
+    EXPECT_EQ(expected.size(), result.size());
+    EXPECT_EQ(expected, result);
+  }
+
+  EXPECT_EQ("abc12345678910111213141516171819202122232425xyz",
+            stringPrintf("abc%d%d%d%d%d%d%d%d%d%d%d%d%d%d"
+                         "%d%d%d%d%d%d%d%d%d%d%dxyz",
+                         1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+                         17, 18, 19, 20, 21, 22, 23, 24, 25));
+}
+
+TEST(StringPrintf, oldStringPrintfTests) {
+  EXPECT_EQ(string("a/b/c/d"),
+            stringPrintf("%s/%s/%s/%s", "a", "b", "c", "d"));
+
+  EXPECT_EQ(string("    5    10"),
+            stringPrintf("%5d %5d", 5, 10));
+
+  // check printing w/ a big buffer
+  for (int size = (1 << 8); size <= (1 << 15); size <<= 1) {
+    string a(size, 'z');
+    string b = stringPrintf("%s", a.c_str());
+    EXPECT_EQ(a.size(), b.size());
+  }
+}
+
+TEST(StringPrintf, oldStringAppendf) {
+  string s = "hello";
+  stringAppendf(&s, "%s/%s/%s/%s", "a", "b", "c", "d");
+  EXPECT_EQ(string("helloa/b/c/d"), s);
+}
+
+// A simple benchmark that tests various output sizes for a simple
+// input; the goal is to measure the output buffer resize code cost.
+void stringPrintfOutputSize(int iters, int param) {
+  string buffer;
+  BENCHMARK_SUSPEND { buffer.resize(param, 'x'); }
+
+  for (int64_t i = 0; i < iters; ++i) {
+    string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str());
+  }
+}
+
+// The first few of these tend to fit in the inline buffer, while the
+// subsequent ones cross that limit, trigger a second vsnprintf, and
+// exercise a different codepath.
+BENCHMARK_PARAM(stringPrintfOutputSize, 1)
+BENCHMARK_PARAM(stringPrintfOutputSize, 4)
+BENCHMARK_PARAM(stringPrintfOutputSize, 16)
+BENCHMARK_PARAM(stringPrintfOutputSize, 64)
+BENCHMARK_PARAM(stringPrintfOutputSize, 256)
+BENCHMARK_PARAM(stringPrintfOutputSize, 1024)
+
+// Benchmark simple stringAppendf behavior to show a pathology Lovro
+// reported (t5735468).
+BENCHMARK(stringPrintfAppendfBenchmark, iters) {
+  for (unsigned int i = 0; i < iters; ++i) {
+    string s;
+    BENCHMARK_SUSPEND { s.reserve(300000); }
+    for (int j = 0; j < 300000; ++j) {
+      stringAppendf(&s, "%d", 1);
+    }
+  }
+}
+
+TEST(Escape, cEscape) {
+  EXPECT_EQ("hello world", cEscape<std::string>("hello world"));
+  EXPECT_EQ("hello \\\\world\\\" goodbye",
+            cEscape<std::string>("hello \\world\" goodbye"));
+  EXPECT_EQ("hello\\nworld", cEscape<std::string>("hello\nworld"));
+  EXPECT_EQ("hello\\377\\376", cEscape<std::string>("hello\xff\xfe"));
+}
+
+TEST(Escape, cUnescape) {
+  EXPECT_EQ("hello world", cUnescape<std::string>("hello world"));
+  EXPECT_EQ("hello \\world\" goodbye",
+            cUnescape<std::string>("hello \\\\world\\\" goodbye"));
+  EXPECT_EQ("hello\nworld", cUnescape<std::string>("hello\\nworld"));
+  EXPECT_EQ("hello\nworld", cUnescape<std::string>("hello\\012world"));
+  EXPECT_EQ("hello\nworld", cUnescape<std::string>("hello\\x0aworld"));
+  EXPECT_EQ("hello\xff\xfe", cUnescape<std::string>("hello\\377\\376"));
+  EXPECT_EQ("hello\xff\xfe", cUnescape<std::string>("hello\\xff\\xfe"));
+
+  EXPECT_THROW({cUnescape<std::string>("hello\\");},
+               std::invalid_argument);
+  EXPECT_THROW({cUnescape<std::string>("hello\\x");},
+               std::invalid_argument);
+  EXPECT_THROW({cUnescape<std::string>("hello\\q");},
+               std::invalid_argument);
+}
+
+TEST(Escape, uriEscape) {
+  EXPECT_EQ("hello%2c%20%2fworld", uriEscape<std::string>("hello, /world"));
+  EXPECT_EQ("hello%2c%20/world", uriEscape<std::string>("hello, /world",
+                                                        UriEscapeMode::PATH));
+  EXPECT_EQ("hello%2c+%2fworld", uriEscape<std::string>("hello, /world",
+                                                        UriEscapeMode::QUERY));
+  EXPECT_EQ(
+    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~",
+    uriEscape<std::string>(
+      "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~")
+  );
+}
+
+TEST(Escape, uriUnescape) {
+  EXPECT_EQ("hello, /world", uriUnescape<std::string>("hello, /world"));
+  EXPECT_EQ("hello, /world", uriUnescape<std::string>("hello%2c%20%2fworld"));
+  EXPECT_EQ("hello,+/world", uriUnescape<std::string>("hello%2c+%2fworld"));
+  EXPECT_EQ("hello, /world", uriUnescape<std::string>("hello%2c+%2fworld",
+                                                      UriEscapeMode::QUERY));
+  EXPECT_EQ("hello/", uriUnescape<std::string>("hello%2f"));
+  EXPECT_EQ("hello/", uriUnescape<std::string>("hello%2F"));
+  EXPECT_THROW({uriUnescape<std::string>("hello%");},
+               std::invalid_argument);
+  EXPECT_THROW({uriUnescape<std::string>("hello%2");},
+               std::invalid_argument);
+  EXPECT_THROW({uriUnescape<std::string>("hello%2g");},
+               std::invalid_argument);
+}
+
+namespace {
+void expectPrintable(StringPiece s) {
+  for (char c : s) {
+    EXPECT_LE(32, c);
+    EXPECT_GE(127, c);
+  }
+}
+}  // namespace
+
+TEST(Escape, uriEscapeAllCombinations) {
+  char c[3];
+  c[2] = '\0';
+  StringPiece in(c, 2);
+  fbstring tmp;
+  fbstring out;
+  for (int i = 0; i < 256; ++i) {
+    c[0] = i;
+    for (int j = 0; j < 256; ++j) {
+      c[1] = j;
+      tmp.clear();
+      out.clear();
+      uriEscape(in, tmp);
+      expectPrintable(tmp);
+      uriUnescape(tmp, out);
+      EXPECT_EQ(in, out);
+    }
+  }
+}
+
+namespace {
+bool isHex(int v) {
+  return ((v >= '0' && v <= '9') ||
+          (v >= 'A' && v <= 'F') ||
+          (v >= 'a' && v <= 'f'));
+}
+}  // namespace
+
+TEST(Escape, uriUnescapePercentDecoding) {
+  char c[4] = {'%', '\0', '\0', '\0'};
+  StringPiece in(c, 3);
+  fbstring out;
+  unsigned int expected = 0;
+  for (int i = 0; i < 256; ++i) {
+    c[1] = i;
+    for (int j = 0; j < 256; ++j) {
+      c[2] = j;
+      if (isHex(i) && isHex(j)) {
+        out.clear();
+        uriUnescape(in, out);
+        EXPECT_EQ(1, out.size());
+        EXPECT_EQ(1, sscanf(c + 1, "%x", &expected));
+        unsigned char v = out[0];
+        EXPECT_EQ(expected, v);
+      } else {
+        EXPECT_THROW({uriUnescape(in, out);}, std::invalid_argument);
+      }
+    }
+  }
+}
+
+namespace {
+fbstring cbmString;
+fbstring cbmEscapedString;
+fbstring cEscapedString;
+fbstring cUnescapedString;
+const size_t kCBmStringLength = 64 << 10;
+const uint32_t kCPrintablePercentage = 90;
+
+fbstring uribmString;
+fbstring uribmEscapedString;
+fbstring uriEscapedString;
+fbstring uriUnescapedString;
+const size_t kURIBmStringLength = 256;
+const uint32_t kURIPassThroughPercentage = 50;
+
+void initBenchmark() {
+  std::mt19937 rnd;
+
+  // C escape
+  std::uniform_int_distribution<uint32_t> printable(32, 126);
+  std::uniform_int_distribution<uint32_t> nonPrintable(0, 160);
+  std::uniform_int_distribution<uint32_t> percentage(0, 99);
+
+  cbmString.reserve(kCBmStringLength);
+  for (size_t i = 0; i < kCBmStringLength; ++i) {
+    unsigned char c;
+    if (percentage(rnd) < kCPrintablePercentage) {
+      c = printable(rnd);
+    } else {
+      c = nonPrintable(rnd);
+      // Generate characters in both non-printable ranges:
+      // 0..31 and 127..255
+      if (c >= 32) {
+        c += (126 - 32) + 1;
+      }
+    }
+    cbmString.push_back(c);
+  }
+
+  cbmEscapedString = cEscape<fbstring>(cbmString);
+
+  // URI escape
+  std::uniform_int_distribution<uint32_t> passthrough('a', 'z');
+  std::string encodeChars = " ?!\"',+[]";
+  std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1);
+
+  uribmString.reserve(kURIBmStringLength);
+  for (size_t i = 0; i < kURIBmStringLength; ++i) {
+    unsigned char c;
+    if (percentage(rnd) < kURIPassThroughPercentage) {
+      c = passthrough(rnd);
+    } else {
+      c = encodeChars[encode(rnd)];
+    }
+    uribmString.push_back(c);
+  }
+
+  uribmEscapedString = uriEscape<fbstring>(uribmString);
+}
+
+BENCHMARK(BM_cEscape, iters) {
+  while (iters--) {
+    cEscapedString = cEscape<fbstring>(cbmString);
+    doNotOptimizeAway(cEscapedString.size());
+  }
+}
+
+BENCHMARK(BM_cUnescape, iters) {
+  while (iters--) {
+    cUnescapedString = cUnescape<fbstring>(cbmEscapedString);
+    doNotOptimizeAway(cUnescapedString.size());
+  }
+}
+
+BENCHMARK(BM_uriEscape, iters) {
+  while (iters--) {
+    uriEscapedString = uriEscape<fbstring>(uribmString);
+    doNotOptimizeAway(uriEscapedString.size());
+  }
+}
+
+BENCHMARK(BM_uriUnescape, iters) {
+  while (iters--) {
+    uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString);
+    doNotOptimizeAway(uriUnescapedString.size());
+  }
+}
+
+}  // namespace
+
+namespace {
+
+double pow2(int exponent) {
+  return double(int64_t(1) << exponent);
+}
+
+}  // namespace
+struct PrettyTestCase{
+  std::string prettyString;
+  double realValue;
+  PrettyType prettyType;
+};
+
+PrettyTestCase prettyTestCases[] =
+{
+  {string("8.53e+07 s "), 85.3e6,  PRETTY_TIME},
+  {string("8.53e+07 s "), 85.3e6,  PRETTY_TIME},
+  {string("85.3 ms"), 85.3e-3,  PRETTY_TIME},
+  {string("85.3 us"), 85.3e-6,  PRETTY_TIME},
+  {string("85.3 ns"), 85.3e-9,  PRETTY_TIME},
+  {string("85.3 ps"), 85.3e-12,  PRETTY_TIME},
+  {string("8.53e-14 s "), 85.3e-15,  PRETTY_TIME},
+
+  {string("0 s "), 0,  PRETTY_TIME},
+  {string("1 s "), 1.0,  PRETTY_TIME},
+  {string("1 ms"), 1.0e-3,  PRETTY_TIME},
+  {string("1 us"), 1.0e-6,  PRETTY_TIME},
+  {string("1 ns"), 1.0e-9,  PRETTY_TIME},
+  {string("1 ps"), 1.0e-12,  PRETTY_TIME},
+
+  // check bytes printing
+  {string("853 B "), 853.,  PRETTY_BYTES},
+  {string("833 kB"), 853.e3,  PRETTY_BYTES},
+  {string("813.5 MB"), 853.e6,  PRETTY_BYTES},
+  {string("7.944 GB"), 8.53e9,  PRETTY_BYTES},
+  {string("794.4 GB"), 853.e9,  PRETTY_BYTES},
+  {string("775.8 TB"), 853.e12,  PRETTY_BYTES},
+
+  {string("0 B "), 0,  PRETTY_BYTES},
+  {string("1 B "), pow2(0),  PRETTY_BYTES},
+  {string("1 kB"), pow2(10),  PRETTY_BYTES},
+  {string("1 MB"), pow2(20),  PRETTY_BYTES},
+  {string("1 GB"), pow2(30),  PRETTY_BYTES},
+  {string("1 TB"), pow2(40),  PRETTY_BYTES},
+
+  {string("853 B  "), 853.,  PRETTY_BYTES_IEC},
+  {string("833 KiB"), 853.e3,  PRETTY_BYTES_IEC},
+  {string("813.5 MiB"), 853.e6,  PRETTY_BYTES_IEC},
+  {string("7.944 GiB"), 8.53e9,  PRETTY_BYTES_IEC},
+  {string("794.4 GiB"), 853.e9,  PRETTY_BYTES_IEC},
+  {string("775.8 TiB"), 853.e12,  PRETTY_BYTES_IEC},
+
+  {string("0 B  "), 0,  PRETTY_BYTES_IEC},
+  {string("1 B  "), pow2(0),  PRETTY_BYTES_IEC},
+  {string("1 KiB"), pow2(10),  PRETTY_BYTES_IEC},
+  {string("1 MiB"), pow2(20),  PRETTY_BYTES_IEC},
+  {string("1 GiB"), pow2(30),  PRETTY_BYTES_IEC},
+  {string("1 TiB"), pow2(40),  PRETTY_BYTES_IEC},
+
+  // check bytes metric printing
+  {string("853 B "), 853.,  PRETTY_BYTES_METRIC},
+  {string("853 kB"), 853.e3,  PRETTY_BYTES_METRIC},
+  {string("853 MB"), 853.e6,  PRETTY_BYTES_METRIC},
+  {string("8.53 GB"), 8.53e9,  PRETTY_BYTES_METRIC},
+  {string("853 GB"), 853.e9,  PRETTY_BYTES_METRIC},
+  {string("853 TB"), 853.e12,  PRETTY_BYTES_METRIC},
+
+  {string("0 B "), 0,  PRETTY_BYTES_METRIC},
+  {string("1 B "), 1.0,  PRETTY_BYTES_METRIC},
+  {string("1 kB"), 1.0e+3,  PRETTY_BYTES_METRIC},
+  {string("1 MB"), 1.0e+6,  PRETTY_BYTES_METRIC},
+
+  {string("1 GB"), 1.0e+9,  PRETTY_BYTES_METRIC},
+  {string("1 TB"), 1.0e+12,  PRETTY_BYTES_METRIC},
+
+  // check metric-units (powers of 1000) printing
+  {string("853  "), 853.,  PRETTY_UNITS_METRIC},
+  {string("853 k"), 853.e3,  PRETTY_UNITS_METRIC},
+  {string("853 M"), 853.e6,  PRETTY_UNITS_METRIC},
+  {string("8.53 bil"), 8.53e9,  PRETTY_UNITS_METRIC},
+  {string("853 bil"), 853.e9,  PRETTY_UNITS_METRIC},
+  {string("853 tril"), 853.e12,  PRETTY_UNITS_METRIC},
+
+  // check binary-units (powers of 1024) printing
+  {string("0  "), 0,  PRETTY_UNITS_BINARY},
+  {string("1  "), pow2(0),  PRETTY_UNITS_BINARY},
+  {string("1 k"), pow2(10),  PRETTY_UNITS_BINARY},
+  {string("1 M"), pow2(20),  PRETTY_UNITS_BINARY},
+  {string("1 G"), pow2(30),  PRETTY_UNITS_BINARY},
+  {string("1 T"), pow2(40),  PRETTY_UNITS_BINARY},
+
+  {string("1023  "), pow2(10) - 1,  PRETTY_UNITS_BINARY},
+  {string("1024 k"), pow2(20) - 1,  PRETTY_UNITS_BINARY},
+  {string("1024 M"), pow2(30) - 1,  PRETTY_UNITS_BINARY},
+  {string("1024 G"), pow2(40) - 1,  PRETTY_UNITS_BINARY},
+
+  {string("0   "), 0,  PRETTY_UNITS_BINARY_IEC},
+  {string("1   "), pow2(0),  PRETTY_UNITS_BINARY_IEC},
+  {string("1 Ki"), pow2(10),  PRETTY_UNITS_BINARY_IEC},
+  {string("1 Mi"), pow2(20),  PRETTY_UNITS_BINARY_IEC},
+  {string("1 Gi"), pow2(30),  PRETTY_UNITS_BINARY_IEC},
+  {string("1 Ti"), pow2(40),  PRETTY_UNITS_BINARY_IEC},
+
+  {string("1023   "), pow2(10) - 1,  PRETTY_UNITS_BINARY_IEC},
+  {string("1024 Ki"), pow2(20) - 1,  PRETTY_UNITS_BINARY_IEC},
+  {string("1024 Mi"), pow2(30) - 1,  PRETTY_UNITS_BINARY_IEC},
+  {string("1024 Gi"), pow2(40) - 1,  PRETTY_UNITS_BINARY_IEC},
+
+  //check border SI cases
+
+  {string("1 Y"), 1e24,  PRETTY_SI},
+  {string("10 Y"), 1e25,  PRETTY_SI},
+  {string("1 y"), 1e-24,  PRETTY_SI},
+  {string("10 y"), 1e-23,  PRETTY_SI},
+
+  // check that negative values work
+  {string("-85.3 s "), -85.3,  PRETTY_TIME},
+  {string("-85.3 ms"), -85.3e-3,  PRETTY_TIME},
+  {string("-85.3 us"), -85.3e-6,  PRETTY_TIME},
+  {string("-85.3 ns"), -85.3e-9,  PRETTY_TIME},
+  // end of test
+  {string("endoftest"), 0, PRETTY_NUM_TYPES}
+};
+
+TEST(PrettyPrint, Basic) {
+  for (int i = 0; prettyTestCases[i].prettyType != PRETTY_NUM_TYPES; ++i){
+    const PrettyTestCase& prettyTest = prettyTestCases[i];
+    EXPECT_EQ(prettyTest.prettyString,
+              prettyPrint(prettyTest.realValue, prettyTest.prettyType));
+  }
+}
+
+TEST(PrettyToDouble, Basic) {
+  // check manually created tests
+  for (int i = 0; prettyTestCases[i].prettyType != PRETTY_NUM_TYPES; ++i){
+    PrettyTestCase testCase = prettyTestCases[i];
+    PrettyType formatType = testCase.prettyType;
+    double x = testCase.realValue;
+    std::string testString = testCase.prettyString;
+    double recoveredX;
+    try{
+      recoveredX = prettyToDouble(testString, formatType);
+    } catch (std::range_error &ex){
+      EXPECT_TRUE(false);
+    }
+    double relativeError = fabs(x) < 1e-5 ? (x-recoveredX) :
+                                            (x - recoveredX) / x;
+    EXPECT_NEAR(0, relativeError, 1e-3);
+  }
+
+  // checks for compatibility with prettyPrint over the whole parameter space
+  for (int i = 0 ; i < PRETTY_NUM_TYPES; ++i){
+    PrettyType formatType = static_cast<PrettyType>(i);
+    for (double x = 1e-18; x < 1e40; x *= 1.9){
+      bool addSpace = static_cast<PrettyType> (i) == PRETTY_SI;
+      for (int it = 0; it < 2; ++it, addSpace = true){
+        double recoveredX;
+        try{
+          recoveredX = prettyToDouble(prettyPrint(x, formatType, addSpace),
+                                             formatType);
+        } catch (std::range_error &ex){
+          EXPECT_TRUE(false);
+        }
+        double relativeError = (x - recoveredX) / x;
+        EXPECT_NEAR(0, relativeError, 1e-3);
+      }
+    }
+  }
+
+  // check for incorrect values
+  EXPECT_THROW(prettyToDouble("10Mx", PRETTY_SI), std::range_error);
+  EXPECT_THROW(prettyToDouble("10 Mx", PRETTY_SI), std::range_error);
+  EXPECT_THROW(prettyToDouble("10 M x", PRETTY_SI), std::range_error);
+
+  StringPiece testString = "10Mx";
+  EXPECT_DOUBLE_EQ(prettyToDouble(&testString, PRETTY_UNITS_METRIC), 10e6);
+  EXPECT_EQ(testString, to_ccs("x"));
+}
+
+TEST(PrettyPrint, HexDump) {
+  std::string a("abc\x00\x02\xa0", 6);  // embedded NUL
+  EXPECT_EQ(
+    "00000000  61 62 63 00 02 a0                                 "
+    "|abc...          |\n",
+    hexDump(a.data(), a.size()));
+
+  a = "abcdefghijklmnopqrstuvwxyz";
+  EXPECT_EQ(
+    "00000000  61 62 63 64 65 66 67 68  69 6a 6b 6c 6d 6e 6f 70  "
+    "|abcdefghijklmnop|\n"
+    "00000010  71 72 73 74 75 76 77 78  79 7a                    "
+    "|qrstuvwxyz      |\n",
+    hexDump(a.data(), a.size()));
+}
+
+TEST(System, errnoStr) {
+  errno = EACCES;
+  EXPECT_EQ(EACCES, errno);
+  EXPECT_EQ(EACCES, errno);  // twice to make sure EXPECT_EQ doesn't change it
+
+  fbstring expected = strerror(ENOENT);
+
+  errno = EACCES;
+  EXPECT_EQ(expected, errnoStr(ENOENT));
+  // Ensure that errno isn't changed
+  EXPECT_EQ(EACCES, errno);
+
+  // Per POSIX, all errno values are positive, so -1 is invalid
+  errnoStr(-1);
+
+  // Ensure that errno isn't changed
+  EXPECT_EQ(EACCES, errno);
+}
+
+namespace {
+
+template<template<class,class> class VectorType>
+void splitTest() {
+  VectorType<string,std::allocator<string> > parts;
+
+  folly::split(',', "a,b,c", parts);
+  EXPECT_EQ(parts.size(), 3);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "b");
+  EXPECT_EQ(parts[2], "c");
+  parts.clear();
+
+  folly::split(',', StringPiece("a,b,c"), parts);
+  EXPECT_EQ(parts.size(), 3);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "b");
+  EXPECT_EQ(parts[2], "c");
+  parts.clear();
+
+  folly::split(',', string("a,b,c"), parts);
+  EXPECT_EQ(parts.size(), 3);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "b");
+  EXPECT_EQ(parts[2], "c");
+  parts.clear();
+
+  folly::split(',', "a,,c", parts);
+  EXPECT_EQ(parts.size(), 3);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "");
+  EXPECT_EQ(parts[2], "c");
+  parts.clear();
+
+  folly::split(',', string("a,,c"), parts);
+  EXPECT_EQ(parts.size(), 3);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "");
+  EXPECT_EQ(parts[2], "c");
+  parts.clear();
+
+  folly::split(',', "a,,c", parts, true);
+  EXPECT_EQ(parts.size(), 2);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "c");
+  parts.clear();
+
+  folly::split(',', string("a,,c"), parts, true);
+  EXPECT_EQ(parts.size(), 2);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "c");
+  parts.clear();
+
+  folly::split(',', string(",,a,,c,,,"), parts, true);
+  EXPECT_EQ(parts.size(), 2);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "c");
+  parts.clear();
+
+  // test multiple split w/o clear
+  folly::split(',', ",,a,,c,,,", parts, true);
+  EXPECT_EQ(parts.size(), 2);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "c");
+  folly::split(',', ",,a,,c,,,", parts, true);
+  EXPECT_EQ(parts.size(), 4);
+  EXPECT_EQ(parts[2], "a");
+  EXPECT_EQ(parts[3], "c");
+  parts.clear();
+
+  // test splits that with multi-line delimiter
+  folly::split("ab", "dabcabkdbkab", parts, true);
+  EXPECT_EQ(parts.size(), 3);
+  EXPECT_EQ(parts[0], "d");
+  EXPECT_EQ(parts[1], "c");
+  EXPECT_EQ(parts[2], "kdbk");
+  parts.clear();
+
+  // test last part is shorter than the delimiter
+  folly::split("bc", "abcd", parts, true);
+  EXPECT_EQ(parts.size(), 2);
+  EXPECT_EQ(parts[0], "a");
+  EXPECT_EQ(parts[1], "d");
+  parts.clear();
+
+  string orig = "ab2342asdfv~~!";
+  folly::split("", orig, parts, true);
+  EXPECT_EQ(parts.size(), 1);
+  EXPECT_EQ(parts[0], orig);
+  parts.clear();
+
+  folly::split("452x;o38asfsajsdlfdf.j", "asfds", parts, true);
+  EXPECT_EQ(parts.size(), 1);
+  EXPECT_EQ(parts[0], "asfds");
+  parts.clear();
+
+  folly::split("a", "", parts, true);
+  EXPECT_EQ(parts.size(), 0);
+  parts.clear();
+
+  folly::split("a", "", parts);
+  EXPECT_EQ(parts.size(), 1);
+  EXPECT_EQ(parts[0], "");
+  parts.clear();
+
+  folly::split("a", StringPiece(), parts, true);
+  EXPECT_EQ(parts.size(), 0);
+  parts.clear();
+
+  folly::split("a", StringPiece(), parts);
+  EXPECT_EQ(parts.size(), 1);
+  EXPECT_EQ(parts[0], "");
+  parts.clear();
+
+  folly::split("a", "abcdefg", parts, true);
+  EXPECT_EQ(parts.size(), 1);
+  EXPECT_EQ(parts[0], "bcdefg");
+  parts.clear();
+
+  orig = "All, , your base, are , , belong to us";
+  folly::split(", ", orig, parts, true);
+  EXPECT_EQ(parts.size(), 4);
+  EXPECT_EQ(parts[0], "All");
+  EXPECT_EQ(parts[1], "your base");
+  EXPECT_EQ(parts[2], "are ");
+  EXPECT_EQ(parts[3], "belong to us");
+  parts.clear();
+  folly::split(", ", orig, parts);
+  EXPECT_EQ(parts.size(), 6);
+  EXPECT_EQ(parts[0], "All");
+  EXPECT_EQ(parts[1], "");
+  EXPECT_EQ(parts[2], "your base");
+  EXPECT_EQ(parts[3], "are ");
+  EXPECT_EQ(parts[4], "");
+  EXPECT_EQ(parts[5], "belong to us");
+  parts.clear();
+
+  orig = ", Facebook, rul,es!, ";
+  folly::split(", ", orig, parts, true);
+  EXPECT_EQ(parts.size(), 2);
+  EXPECT_EQ(parts[0], "Facebook");
+  EXPECT_EQ(parts[1], "rul,es!");
+  parts.clear();
+  folly::split(", ", orig, parts);
+  EXPECT_EQ(parts.size(), 4);
+  EXPECT_EQ(parts[0], "");
+  EXPECT_EQ(parts[1], "Facebook");
+  EXPECT_EQ(parts[2], "rul,es!");
+  EXPECT_EQ(parts[3], "");
+}
+
+template<template<class,class> class VectorType>
+void piecesTest() {
+  VectorType<StringPiece,std::allocator<StringPiece> > pieces;
+  VectorType<StringPiece,std::allocator<StringPiece> > pieces2;
+
+  folly::split(',', "a,b,c", pieces);
+  EXPECT_EQ(pieces.size(), 3);
+  EXPECT_EQ(pieces[0], to_ccs("a"));
+  EXPECT_EQ(pieces[1], to_ccs("b"));
+  EXPECT_EQ(pieces[2], to_ccs("c"));
+
+  pieces.clear();
+
+  folly::split(',', "a,,c", pieces);
+  EXPECT_EQ(pieces.size(), 3);
+  EXPECT_EQ(pieces[0], to_ccs("a"));
+  EXPECT_EQ(pieces[1], to_ccs(""));
+  EXPECT_EQ(pieces[2], to_ccs("c"));
+  pieces.clear();
+
+  folly::split(',', "a,,c", pieces, true);
+  EXPECT_EQ(pieces.size(), 2);
+  EXPECT_EQ(pieces[0], to_ccs("a"));
+  EXPECT_EQ(pieces[1], to_ccs("c"));
+  pieces.clear();
+
+  folly::split(',', ",,a,,c,,,", pieces, true);
+  EXPECT_EQ(pieces.size(), 2);
+  EXPECT_EQ(pieces[0], to_ccs("a"));
+  EXPECT_EQ(pieces[1], to_ccs("c"));
+  pieces.clear();
+
+  // test multiple split w/o clear
+  folly::split(',', ",,a,,c,,,", pieces, true);
+  EXPECT_EQ(pieces.size(), 2);
+  EXPECT_EQ(pieces[0], to_ccs("a"));
+  EXPECT_EQ(pieces[1], to_ccs("c"));
+  folly::split(',', ",,a,,c,,,", pieces, true);
+  EXPECT_EQ(pieces.size(), 4);
+  EXPECT_EQ(pieces[2], to_ccs("a"));
+  EXPECT_EQ(pieces[3], to_ccs("c"));
+  pieces.clear();
+
+  // test multiple split rounds
+  folly::split(",", "a_b,c_d", pieces);
+  EXPECT_EQ(pieces.size(), 2);
+  EXPECT_EQ(pieces[0], to_ccs("a_b"));
+  EXPECT_EQ(pieces[1], to_ccs("c_d"));
+  folly::split("_", pieces[0], pieces2);
+  EXPECT_EQ(pieces2.size(), 2);
+  EXPECT_EQ(pieces2[0], to_ccs("a"));
+  EXPECT_EQ(pieces2[1], to_ccs("b"));
+  pieces2.clear();
+  folly::split("_", pieces[1], pieces2);
+  EXPECT_EQ(pieces2.size(), 2);
+  EXPECT_EQ(pieces2[0], to_ccs("c"));
+  EXPECT_EQ(pieces2[1], to_ccs("d"));
+  pieces.clear();
+  pieces2.clear();
+
+  // test splits that with multi-line delimiter
+  folly::split("ab", "dabcabkdbkab", pieces, true);
+  EXPECT_EQ(pieces.size(), 3);
+  EXPECT_EQ(pieces[0], to_ccs("d"));
+  EXPECT_EQ(pieces[1], to_ccs("c"));
+  EXPECT_EQ(pieces[2], to_ccs("kdbk"));
+  pieces.clear();
+
+  string orig = "ab2342asdfv~~!";
+  folly::split("", orig.c_str(), pieces, true);
+  EXPECT_EQ(pieces.size(), 1);
+  EXPECT_EQ(pieces[0], orig);
+  pieces.clear();
+
+  folly::split("452x;o38asfsajsdlfdf.j", "asfds", pieces, true);
+  EXPECT_EQ(pieces.size(), 1);
+  EXPECT_EQ(pieces[0], to_ccs("asfds"));
+  pieces.clear();
+
+  folly::split("a", "", pieces, true);
+  EXPECT_EQ(pieces.size(), 0);
+  pieces.clear();
+
+  folly::split("a", "", pieces);
+  EXPECT_EQ(pieces.size(), 1);
+  EXPECT_EQ(pieces[0], to_ccs(""));
+  pieces.clear();
+
+  folly::split("a", "abcdefg", pieces, true);
+  EXPECT_EQ(pieces.size(), 1);
+  EXPECT_EQ(pieces[0], to_ccs("bcdefg"));
+  pieces.clear();
+
+  orig = "All, , your base, are , , belong to us";
+  folly::split(", ", orig, pieces, true);
+  EXPECT_EQ(pieces.size(), 4);
+  EXPECT_EQ(pieces[0], to_ccs("All"));
+  EXPECT_EQ(pieces[1], to_ccs("your base"));
+  EXPECT_EQ(pieces[2], to_ccs("are "));
+  EXPECT_EQ(pieces[3], to_ccs("belong to us"));
+  pieces.clear();
+  folly::split(", ", orig, pieces);
+  EXPECT_EQ(pieces.size(), 6);
+  EXPECT_EQ(pieces[0], to_ccs("All"));
+  EXPECT_EQ(pieces[1], to_ccs(""));
+  EXPECT_EQ(pieces[2], to_ccs("your base"));
+  EXPECT_EQ(pieces[3], to_ccs("are "));
+  EXPECT_EQ(pieces[4], to_ccs(""));
+  EXPECT_EQ(pieces[5], to_ccs("belong to us"));
+  pieces.clear();
+
+  orig = ", Facebook, rul,es!, ";
+  folly::split(", ", orig, pieces, true);
+  EXPECT_EQ(pieces.size(), 2);
+  EXPECT_EQ(pieces[0], to_ccs("Facebook"));
+  EXPECT_EQ(pieces[1], to_ccs("rul,es!"));
+  pieces.clear();
+  folly::split(", ", orig, pieces);
+  EXPECT_EQ(pieces.size(), 4);
+  EXPECT_EQ(pieces[0], to_ccs(""));
+  EXPECT_EQ(pieces[1], to_ccs("Facebook"));
+  EXPECT_EQ(pieces[2], to_ccs("rul,es!"));
+  EXPECT_EQ(pieces[3], to_ccs(""));
+  pieces.clear();
+
+  const char* str = "a,b";
+  folly::split(',', StringPiece(str), pieces);
+  EXPECT_EQ(pieces.size(), 2);
+  EXPECT_EQ(pieces[0], to_ccs("a"));
+  EXPECT_EQ(pieces[1], to_ccs("b"));
+  EXPECT_EQ(pieces[0].start(), str);
+  EXPECT_EQ(pieces[1].start(), str + 2);
+
+  std::set<StringPiece> unique;
+  folly::splitTo<StringPiece>(":", "asd:bsd:asd:asd:bsd:csd::asd",
+    std::inserter(unique, unique.begin()), true);
+  EXPECT_EQ(unique.size(), 3);
+  if (unique.size() == 3) {
+    EXPECT_EQ(*unique.begin(), to_ccs("asd"));
+    EXPECT_EQ(*--unique.end(), to_ccs("csd"));
+  }
+
+  VectorType<fbstring,std::allocator<fbstring> > blah;
+  folly::split('-', "a-b-c-d-f-e", blah);
+  EXPECT_EQ(blah.size(), 6);
+}
+
+}
+
+TEST(Split, split_vector) {
+  splitTest<std::vector>();
+}
+TEST(Split, split_fbvector) {
+  splitTest<folly::fbvector>();
+}
+TEST(Split, pieces_vector) {
+  piecesTest<std::vector>();
+}
+TEST(Split, pieces_fbvector) {
+  piecesTest<folly::fbvector>();
+}
+
+TEST(Split, fixed) {
+  StringPiece a, b, c, d;
+
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c.d", a, b, c, d));
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c));
+  EXPECT_TRUE(folly::split<false>('.', "a.b", a, b));
+  EXPECT_TRUE(folly::split<false>('.', "a", a));
+
+  EXPECT_TRUE(folly::split('.', "a.b.c.d", a, b, c, d));
+  EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c));
+  EXPECT_TRUE(folly::split('.', "a.b", a, b));
+  EXPECT_TRUE(folly::split('.', "a", a));
+
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_EQ(to_ccs("c"), c);
+  EXPECT_FALSE(folly::split<false>('.', "a.b", a, b, c));
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b.c"), b);
+
+  EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_EQ(to_ccs("c"), c);
+  EXPECT_FALSE(folly::split('.', "a.b.c", a, b));
+  EXPECT_FALSE(folly::split('.', "a.b", a, b, c));
+
+  EXPECT_TRUE(folly::split<false>('.', "a.b", a, b));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_FALSE(folly::split<false>('.', "a", a, b));
+  EXPECT_TRUE(folly::split<false>('.', "a.b", a));
+  EXPECT_EQ(to_ccs("a.b"), a);
+
+  EXPECT_TRUE(folly::split('.', "a.b", a, b));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_FALSE(folly::split('.', "a", a, b));
+  EXPECT_FALSE(folly::split('.', "a.b", a));
+}
+
+TEST(Split, std_string_fixed) {
+  std::string a, b, c, d;
+
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c.d", a, b, c, d));
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c));
+  EXPECT_TRUE(folly::split<false>('.', "a.b", a, b));
+  EXPECT_TRUE(folly::split<false>('.', "a", a));
+
+  EXPECT_TRUE(folly::split('.', "a.b.c.d", a, b, c, d));
+  EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c));
+  EXPECT_TRUE(folly::split('.', "a.b", a, b));
+  EXPECT_TRUE(folly::split('.', "a", a));
+
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b, c));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_EQ(to_ccs("c"), c);
+  EXPECT_FALSE(folly::split<false>('.', "a.b", a, b, c));
+  EXPECT_TRUE(folly::split<false>('.', "a.b.c", a, b));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b.c"), b);
+
+  EXPECT_TRUE(folly::split('.', "a.b.c", a, b, c));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_EQ(to_ccs("c"), c);
+  EXPECT_FALSE(folly::split('.', "a.b.c", a, b));
+  EXPECT_FALSE(folly::split('.', "a.b", a, b, c));
+
+  EXPECT_TRUE(folly::split<false>('.', "a.b", a, b));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_FALSE(folly::split<false>('.', "a", a, b));
+  EXPECT_TRUE(folly::split<false>('.', "a.b", a));
+  EXPECT_EQ(to_ccs("a.b"), a);
+
+  EXPECT_TRUE(folly::split('.', "a.b", a, b));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(to_ccs("b"), b);
+  EXPECT_FALSE(folly::split('.', "a", a, b));
+  EXPECT_FALSE(folly::split('.', "a.b", a));
+}
+
+TEST(Split, fixed_convert) {
+  StringPiece a, d;
+  int b;
+  double c;
+
+  EXPECT_TRUE(folly::split(':', "a:13:14.7:b", a, b, c, d));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(13, b);
+  EXPECT_NEAR(14.7, c, 1e-10);
+  EXPECT_EQ(to_ccs("b"), d);
+
+  EXPECT_TRUE(folly::split<false>(':', "b:14:15.3:c", a, b, c, d));
+  EXPECT_EQ(to_ccs("b"), a);
+  EXPECT_EQ(14, b);
+  EXPECT_NEAR(15.3, c, 1e-10);
+  EXPECT_EQ(to_ccs("c"), d);
+
+  EXPECT_FALSE(folly::split(':', "a:13:14.7:b", a, b, d));
+
+  EXPECT_TRUE(folly::split<false>(':', "a:13:14.7:b", a, b, d));
+  EXPECT_EQ(to_ccs("a"), a);
+  EXPECT_EQ(13, b);
+  EXPECT_EQ(to_ccs("14.7:b"), d);
+
+  EXPECT_THROW(folly::split<false>(':', "a:13:14.7:b", a, b, c),
+               std::range_error);
+}
+
+TEST(String, join) {
+  string output;
+
+  std::vector<int> empty = { };
+  join(":", empty, output);
+  EXPECT_TRUE(output.empty());
+
+  std::vector<std::string> input1 = { "1", "23", "456", "" };
+  join(':', input1, output);
+  EXPECT_EQ(output, "1:23:456:");
+  output = join(':', input1);
+  EXPECT_EQ(output, "1:23:456:");
+
+  auto input2 = { 1, 23, 456 };
+  join("-*-", input2, output);
+  EXPECT_EQ(output, "1-*-23-*-456");
+  output = join("-*-", input2);
+  EXPECT_EQ(output, "1-*-23-*-456");
+
+  auto input3 = { 'f', 'a', 'c', 'e', 'b', 'o', 'o', 'k' };
+  join("", input3, output);
+  EXPECT_EQ(output, "facebook");
+
+  join("_", { "", "f", "a", "c", "e", "b", "o", "o", "k", "" }, output);
+  EXPECT_EQ(output, "_f_a_c_e_b_o_o_k_");
+
+  output = join("", input3.begin(), input3.end());
+  EXPECT_EQ(output, "facebook");
+}
+
+TEST(String, hexlify) {
+  string input1 = "0123";
+  string output1;
+  EXPECT_TRUE(hexlify(input1, output1));
+  EXPECT_EQ(output1, "30313233");
+
+  fbstring input2 = "abcdefg";
+  input2[1] = 0;
+  input2[3] = 0xff;
+  input2[5] = 0xb6;
+  fbstring output2;
+  EXPECT_TRUE(hexlify(input2, output2));
+  EXPECT_EQ(output2, "610063ff65b667");
+}
+
+TEST(String, unhexlify) {
+  string input1 = "30313233";
+  string output1;
+  EXPECT_TRUE(unhexlify(input1, output1));
+  EXPECT_EQ(output1, "0123");
+
+  fbstring input2 = "610063ff65b667";
+  fbstring output2;
+  EXPECT_TRUE(unhexlify(input2, output2));
+  EXPECT_EQ(output2.size(), 7);
+  EXPECT_EQ(output2[0], 'a');
+  EXPECT_EQ(output2[1], 0);
+  EXPECT_EQ(output2[2], 'c');
+  EXPECT_EQ(output2[3] & 0xff, 0xff);
+  EXPECT_EQ(output2[4], 'e');
+  EXPECT_EQ(output2[5] & 0xff, 0xb6);
+  EXPECT_EQ(output2[6], 'g');
+
+  string input3 = "x";
+  string output3;
+  EXPECT_FALSE(unhexlify(input3, output3));
+
+  string input4 = "xy";
+  string output4;
+  EXPECT_FALSE(unhexlify(input4, output4));
+}
+
+TEST(String, backslashify) {
+  EXPECT_EQ(to_ccs("abc"), string("abc"));
+  EXPECT_EQ(to_ccs("abc"), backslashify(string("abc")));
+  EXPECT_EQ(to_ccs("abc\\r"), backslashify(string("abc\r")));
+  EXPECT_EQ(to_ccs("abc\\x0d"), backslashify(string("abc\r"), true));
+  EXPECT_EQ(to_ccs("\\0\\0"), backslashify(string(2, '\0')));
+}
+
+TEST(String, humanify) {
+  // Simple cases; output is obvious.
+  EXPECT_EQ("abc", humanify(string("abc")));
+  EXPECT_EQ("abc\\\\r", humanify(string("abc\\r")));
+  EXPECT_EQ("0xff", humanify(string("\xff")));
+  EXPECT_EQ("abc\\xff", humanify(string("abc\xff")));
+  EXPECT_EQ("abc\\b", humanify(string("abc\b")));
+  EXPECT_EQ("0x00", humanify(string(1, '\0')));
+  EXPECT_EQ("0x0000", humanify(string(2, '\0')));
+
+
+  // Mostly printable, so backslash!  80, 60, and 40% printable, respectively
+  EXPECT_EQ("aaaa\\xff", humanify(string("aaaa\xff")));
+  EXPECT_EQ("aaa\\xff\\xff", humanify(string("aaa\xff\xff")));
+  EXPECT_EQ("aa\\xff\\xff\\xff", humanify(string("aa\xff\xff\xff")));
+
+  // 20% printable, and the printable portion isn't the prefix; hexify!
+  EXPECT_EQ("0xff61ffffff", humanify(string("\xff" "a\xff\xff\xff")));
+
+  // Same as previous, except swap first two chars; prefix is
+  // printable and within the threshold, so backslashify.
+  EXPECT_EQ("a\\xff\\xff\\xff\\xff", humanify(string("a\xff\xff\xff\xff")));
+
+  // Just too much unprintable; hex, despite prefix.
+  EXPECT_EQ("0x61ffffffffff", humanify(string("a\xff\xff\xff\xff\xff")));
+}
+
+namespace {
+
+/**
+ * Copy bytes from src to somewhere in the buffer referenced by dst. The
+ * actual starting position of the copy will be the first address in the
+ * destination buffer whose address mod 8 is equal to the src address mod 8.
+ * The caller is responsible for ensuring that the destination buffer has
+ * enough extra space to accommodate the shifted copy.
+ */
+char* copyWithSameAlignment(char* dst, const char* src, size_t length) {
+  const char* originalDst = dst;
+  size_t dstOffset = size_t(dst) & 0x7;
+  size_t srcOffset = size_t(src) & 0x7;
+  while (dstOffset != srcOffset) {
+    dst++;
+    dstOffset++;
+    dstOffset &= 0x7;
+  }
+  CHECK(dst <= originalDst + 7);
+  CHECK((size_t(dst) & 0x7) == (size_t(src) & 0x7));
+  memcpy(dst, src, length);
+  return dst;
+}
+
+void testToLowerAscii(Range<const char*> src) {
+  // Allocate extra space so we can make copies that start at the
+  // same alignment (byte, word, quadword, etc) as the source buffer.
+  string controlBuf(src.size() + 7, '\0');
+  char* control = copyWithSameAlignment(const_cast<char *>(controlBuf.data()), src.begin(), src.size());
+
+  string testBuf(src.size() + 7, '\0');
+  char* test = copyWithSameAlignment(const_cast<char *>(testBuf.data()), src.begin(), src.size());
+
+  for (size_t i = 0; i < src.size(); i++) {
+    control[i] = tolower(control[i]);
+  }
+  toLowerAscii(test, src.size());
+  for (size_t i = 0; i < src.size(); i++) {
+    EXPECT_EQ(control[i], test[i]);
+  }
+}
+
+} // anon namespace
+
+TEST(String, toLowerAsciiAligned) {
+  static const size_t kSize = 256;
+  char input[kSize];
+  for (size_t i = 0; i < kSize; i++) {
+    input[i] = (char)(i & 0xff);
+  }
+  testToLowerAscii(Range<const char*>(input, kSize));
+}
+
+TEST(String, toLowerAsciiUnaligned) {
+  static const size_t kSize = 256;
+  char input[kSize];
+  for (size_t i = 0; i < kSize; i++) {
+    input[i] = (char)(i & 0xff);
+  }
+  // Test input buffers of several lengths to exercise all the
+  // cases: buffer at the start/middle/end of an aligned block, plus
+  // buffers that span multiple aligned blocks.  The longest test input
+  // is 3 unaligned bytes + 4 32-bit aligned bytes + 8 64-bit aligned
+  // + 4 32-bit aligned + 3 unaligned = 22 bytes.
+  for (size_t length = 1; length < 23; length++) {
+    for (size_t offset = 0; offset + length <= kSize; offset++) {
+      testToLowerAscii(Range<const char*>(input + offset, length));
+    }
+  }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+BENCHMARK(splitOnSingleChar, iters) {
+  static const std::string line = "one:two:three:four";
+  for (size_t i = 0; i < iters << 4; ++i) {
+    std::vector<StringPiece> pieces;
+    folly::split(':', line, pieces);
+  }
+}
+
+BENCHMARK(splitOnSingleCharFixed, iters) {
+  static const std::string line = "one:two:three:four";
+  for (size_t i = 0; i < iters << 4; ++i) {
+    StringPiece a, b, c, d;
+    folly::split(':', line, a, b, c, d);
+  }
+}
+
+BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
+  static const std::string line = "one:two:three:four";
+  for (size_t i = 0; i < iters << 4; ++i) {
+    StringPiece a, b, c, d;
+    folly::split<false>(':', line, a, b, c, d);
+  }
+}
+
+BENCHMARK(splitStr, iters) {
+  static const std::string line = "one-*-two-*-three-*-four";
+  for (size_t i = 0; i < iters << 4; ++i) {
+    std::vector<StringPiece> pieces;
+    folly::split("-*-", line, pieces);
+  }
+}
+
+BENCHMARK(splitStrFixed, iters) {
+  static const std::string line = "one-*-two-*-three-*-four";
+  for (size_t i = 0; i < iters << 4; ++i) {
+    StringPiece a, b, c, d;
+    folly::split("-*-", line, a, b, c, d);
+  }
+}
+
+BENCHMARK(boost_splitOnSingleChar, iters) {
+  static const std::string line = "one:two:three:four";
+  bool(*pred)(char) = [] (char c) -> bool { return c == ':'; };
+  for (size_t i = 0; i < iters << 4; ++i) {
+    std::vector<boost::iterator_range<std::string::const_iterator> > pieces;
+    boost::split(pieces, line, pred);
+  }
+}
+
+BENCHMARK(joinCharStr, iters) {
+  static const std::vector<std::string> input = {
+    "one", "two", "three", "four", "five", "six", "seven" };
+  for (size_t i = 0; i < iters << 4; ++i) {
+    std::string output;
+    folly::join(':', input, output);
+  }
+}
+
+BENCHMARK(joinStrStr, iters) {
+  static const std::vector<std::string> input = {
+    "one", "two", "three", "four", "five", "six", "seven" };
+  for (size_t i = 0; i < iters << 4; ++i) {
+    std::string output;
+    folly::join(":", input, output);
+  }
+}
+
+BENCHMARK(joinInt, iters) {
+  static const auto input = {
+    123, 456, 78910, 1112, 1314, 151, 61718 };
+  for (size_t i = 0; i < iters << 4; ++i) {
+    std::string output;
+    folly::join(":", input, output);
+  }
+}
+
+TEST(String, whitespace) {
+  // trimWhitespace:
+  EXPECT_EQ(to_ccs("kavabanga"),
+    trimWhitespace("kavabanga"));
+  EXPECT_EQ(to_ccs("kavabanga"),
+    trimWhitespace("kavabanga \t \n  "));
+  EXPECT_EQ(to_ccs("kavabanga"),
+    trimWhitespace("   \t \r \n \n kavabanga"));
+  EXPECT_EQ(to_ccs("kavabanga"),
+    trimWhitespace("\t \r \n   kavabanga \t \n  "));
+  EXPECT_EQ(to_ccs("kavabanga"),
+    trimWhitespace("   \t \r \n \n kavabanga"));
+  EXPECT_EQ(to_ccs("kavabanga"),
+    trimWhitespace("\t \r \n   kavabanga \t \n  "));
+  EXPECT_EQ(
+    ltrimWhitespace(rtrimWhitespace("kavabanga")),
+    rtrimWhitespace(ltrimWhitespace("kavabanga")));
+  EXPECT_EQ(
+    ltrimWhitespace(rtrimWhitespace("kavabanga  \r\t\n")),
+    rtrimWhitespace(ltrimWhitespace("kavabanga  \r\t\n")));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\t \r \n   \t \n  "));
+  EXPECT_EQ(to_ccs(""), trimWhitespace(""));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\t"));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\r"));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\n"));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\t "));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\r  "));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("\n   "));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("    \t"));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("    \r"));
+  EXPECT_EQ(to_ccs(""), trimWhitespace("    \n"));
+
+  // ltrimWhitespace:
+  EXPECT_EQ(to_ccs("kavabanga"), ltrimWhitespace("\t kavabanga"));
+  EXPECT_EQ(to_ccs("kavabanga \r\n"), ltrimWhitespace("\t kavabanga \r\n"));
+  EXPECT_EQ(to_ccs(""), ltrimWhitespace("\r "));
+  EXPECT_EQ(to_ccs(""), ltrimWhitespace("\n   "));
+  EXPECT_EQ(to_ccs(""), ltrimWhitespace("\r   "));
+
+  // rtrimWhitespace:
+  EXPECT_EQ(to_ccs("\t kavabanga"), rtrimWhitespace("\t kavabanga"));
+  EXPECT_EQ(to_ccs("\t kavabanga"), rtrimWhitespace("\t kavabanga \r\n"));
+  EXPECT_EQ(to_ccs(""), rtrimWhitespace("\r "));
+  EXPECT_EQ(to_ccs(""), rtrimWhitespace("\n   "));
+  EXPECT_EQ(to_ccs(""), rtrimWhitespace("\r   "));
+}
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  auto ret = RUN_ALL_TESTS();
+  if (!ret) {
+    initBenchmark();
+    if (FLAGS_benchmark) {
+      folly::runBenchmarks();
+    }
+  }
+  return ret;
+}
diff --git a/faux-folly/folly/test/SynchronizedTestLib.h b/faux-folly/folly/test/SynchronizedTestLib.h
new file mode 100644
index 0000000..099efe9
--- /dev/null
+++ b/faux-folly/folly/test/SynchronizedTestLib.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef FOLLY_TEST_SYNCHRONIZEDTESTLIB_H
+#define FOLLY_TEST_SYNCHRONIZEDTESTLIB_H
+
+// We have mutex types outside of folly that we want to test with Synchronized.
+// Make it easy for mutex implementators to test their classes with
+// Synchronized by just having a test like:
+//
+// class MyMutex { ... };
+//
+// TEST(Synchronized, Basic) {
+//   testBasic<MyMutex>();
+// }
+//
+// ... similar for testConcurrency, testDualLocking, etc.
+
+
+template <class Mutex> void testBasic();
+
+template <class Mutex> void testConcurrency();
+
+template <class Mutex> void testDualLocking();
+
+template <class Mutex> void testDualLockingWithConst();
+
+template <class Mutex> void testTimedSynchronized();
+
+template <class Mutex> void testTimedSynchronizedWithConst();
+
+template <class Mutex> void testConstCopy();
+
+#include <folly/test/SynchronizedTestLib-inl.h>
+
+#endif /* FOLLY_TEST_SYNCHRONIZEDTESTLIB_H */
diff --git a/faux-folly/folly/test/ThreadCachedIntTest.cpp b/faux-folly/folly/test/ThreadCachedIntTest.cpp
new file mode 100644
index 0000000..330d710
--- /dev/null
+++ b/faux-folly/folly/test/ThreadCachedIntTest.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2015 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/ThreadCachedInt.h>
+#include <folly/Hash.h>
+
+#include <atomic>
+#include <thread>
+#include <gtest/gtest.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include <folly/Benchmark.h>
+
+using namespace folly;
+
+TEST(ThreadCachedInt, SingleThreadedNotCached) {
+  ThreadCachedInt<int64_t> val(0, 0);
+  EXPECT_EQ(0, val.readFast());
+  ++val;
+  EXPECT_EQ(1, val.readFast());
+  for (int i = 0; i < 41; ++i) {
+    val.increment(1);
+  }
+  EXPECT_EQ(42, val.readFast());
+  --val;
+  EXPECT_EQ(41, val.readFast());
+}
+
+// Note: This is somewhat fragile to the implementation.  If this causes
+// problems, feel free to remove it.
+TEST(ThreadCachedInt, SingleThreadedCached) {
+  ThreadCachedInt<int64_t> val(0, 10);
+  EXPECT_EQ(0, val.readFast());
+  ++val;
+  EXPECT_EQ(0, val.readFast());
+  for (int i = 0; i < 7; ++i) {
+    val.increment(1);
+  }
+  EXPECT_EQ(0, val.readFast());
+  EXPECT_EQ(0, val.readFastAndReset());
+  EXPECT_EQ(8, val.readFull());
+  EXPECT_EQ(8, val.readFullAndReset());
+  EXPECT_EQ(0, val.readFull());
+  EXPECT_EQ(0, val.readFast());
+}
+
+ThreadCachedInt<int32_t> globalInt32(0, 11);
+ThreadCachedInt<int64_t> globalInt64(0, 11);
+int kNumInserts = 100000;
+DEFINE_int32(numThreads, 8, "Number simultaneous threads for benchmarks.");
+#define CREATE_INC_FUNC(size)                                       \
+  void incFunc ## size () {                                         \
+    const int num = kNumInserts / FLAGS_numThreads;                 \
+    for (int i = 0; i < num; ++i) {                                 \
+      ++globalInt ## size ;                                         \
+    }                                                               \
+  }
+CREATE_INC_FUNC(64);
+CREATE_INC_FUNC(32);
+
+// Confirms counts are accurate with competing threads
+TEST(ThreadCachedInt, MultiThreadedCached) {
+  kNumInserts = 100000;
+  CHECK_EQ(0, kNumInserts % FLAGS_numThreads) <<
+    "FLAGS_numThreads must evenly divide kNumInserts (" << kNumInserts << ").";
+  const int numPerThread = kNumInserts / FLAGS_numThreads;
+  ThreadCachedInt<int64_t> TCInt64(0, numPerThread - 2);
+  {
+    std::atomic<bool> run(true);
+    std::atomic<int> threadsDone(0);
+    std::vector<std::thread> threads;
+    for (int i = 0; i < FLAGS_numThreads; ++i) {
+      threads.push_back(std::thread([&] {
+        FOR_EACH_RANGE(k, 0, numPerThread) {
+          ++TCInt64;
+        }
+        std::atomic_fetch_add(&threadsDone, 1);
+        while (run.load()) { usleep(100); }
+      }));
+    }
+
+    // We create and increment another ThreadCachedInt here to make sure it
+    // doesn't interact with the other instances
+    ThreadCachedInt<int64_t> otherTCInt64(0, 10);
+    otherTCInt64.set(33);
+    ++otherTCInt64;
+
+    while (threadsDone.load() < FLAGS_numThreads) { usleep(100); }
+
+    ++otherTCInt64;
+
+    // Threads are done incrementing, but caches have not been flushed yet, so
+    // we have to readFull.
+    EXPECT_NE(kNumInserts, TCInt64.readFast());
+    EXPECT_EQ(kNumInserts, TCInt64.readFull());
+
+    run.store(false);
+    for (auto& t : threads) {
+      t.join();
+    }
+
+  }  // Caches are flushed when threads finish
+  EXPECT_EQ(kNumInserts, TCInt64.readFast());
+}
+
+#define MAKE_MT_CACHE_SIZE_BM(size)                             \
+  void BM_mt_cache_size ## size (int iters, int cacheSize) {    \
+    kNumInserts = iters;                                        \
+    globalInt ## size.set(0);                                   \
+    globalInt ## size.setCacheSize(cacheSize);                  \
+    std::vector<std::thread> threads;                           \
+    for (int i = 0; i < FLAGS_numThreads; ++i) {                \
+      threads.push_back(std::thread(incFunc ## size));          \
+    }                                                           \
+    for (auto& t : threads) {                                   \
+      t.join();                                                 \
+    }                                                           \
+  }
+MAKE_MT_CACHE_SIZE_BM(64);
+MAKE_MT_CACHE_SIZE_BM(32);
+
+#define REG_BASELINE(name, inc_stmt)                            \
+  BENCHMARK(FB_CONCATENATE(BM_mt_baseline_, name), iters) {     \
+    const int iterPerThread = iters / FLAGS_numThreads;         \
+    std::vector<std::thread> threads;                           \
+    for (int i = 0; i < FLAGS_numThreads; ++i) {                \
+      threads.push_back(std::thread([&]() {                     \
+            for (int i = 0; i < iterPerThread; ++i) {           \
+              inc_stmt;                                         \
+            }                                                   \
+          }));                                                  \
+    }                                                           \
+    for (auto& t : threads) {                                   \
+      t.join();                                                 \
+    }                                                           \
+  }
+
+ThreadLocal<int64_t> globalTL64Baseline;
+ThreadLocal<int32_t> globalTL32Baseline;
+std::atomic<int64_t> globalInt64Baseline(0);
+std::atomic<int32_t> globalInt32Baseline(0);
+FOLLY_TLS int64_t global__thread64;
+FOLLY_TLS int32_t global__thread32;
+
+// Alternate lock-free implementation.  Achieves about the same performance,
+// but uses about 20x more memory than ThreadCachedInt with 24 threads.
+struct ShardedAtomicInt {
+  static const int64_t kBuckets_ = 2048;
+  std::atomic<int64_t> ints_[kBuckets_];
+
+  inline void inc(int64_t val = 1) {
+    int bucket = hash::twang_mix64(
+      uint64_t(pthread_self())) & (kBuckets_ - 1);
+    std::atomic_fetch_add(&ints_[bucket], val);
+  }
+
+  // read the first few and extrapolate
+  int64_t readFast() {
+    int64_t ret = 0;
+    static const int numToRead = 8;
+    FOR_EACH_RANGE(i, 0, numToRead) {
+      ret += ints_[i].load(std::memory_order_relaxed);
+    }
+    return ret * (kBuckets_ / numToRead);
+  }
+
+  // readFull is lock-free, but has to do thousands of loads...
+  int64_t readFull() {
+    int64_t ret = 0;
+    for (auto& i : ints_) {
+      // Fun fact - using memory_order_consume below reduces perf 30-40% in high
+      // contention benchmarks.
+      ret += i.load(std::memory_order_relaxed);
+    }
+    return ret;
+  }
+};
+ShardedAtomicInt shd_int64;
+
+REG_BASELINE(_thread64, global__thread64 += 1);
+REG_BASELINE(_thread32, global__thread32 += 1);
+REG_BASELINE(ThreadLocal64, *globalTL64Baseline += 1);
+REG_BASELINE(ThreadLocal32, *globalTL32Baseline += 1);
+REG_BASELINE(atomic_inc64,
+             std::atomic_fetch_add(&globalInt64Baseline, int64_t(1)));
+REG_BASELINE(atomic_inc32,
+             std::atomic_fetch_add(&globalInt32Baseline, int32_t(1)));
+REG_BASELINE(ShardedAtm64, shd_int64.inc());
+
+BENCHMARK_PARAM(BM_mt_cache_size64, 0);
+BENCHMARK_PARAM(BM_mt_cache_size64, 10);
+BENCHMARK_PARAM(BM_mt_cache_size64, 100);
+BENCHMARK_PARAM(BM_mt_cache_size64, 1000);
+BENCHMARK_PARAM(BM_mt_cache_size32, 0);
+BENCHMARK_PARAM(BM_mt_cache_size32, 10);
+BENCHMARK_PARAM(BM_mt_cache_size32, 100);
+BENCHMARK_PARAM(BM_mt_cache_size32, 1000);
+BENCHMARK_DRAW_LINE();
+
+// single threaded
+BENCHMARK(Atomic_readFull) {
+  doNotOptimizeAway(globalInt64Baseline.load(std::memory_order_relaxed));
+}
+BENCHMARK(ThrCache_readFull) {
+  doNotOptimizeAway(globalInt64.readFull());
+}
+BENCHMARK(Sharded_readFull) {
+  doNotOptimizeAway(shd_int64.readFull());
+}
+BENCHMARK(ThrCache_readFast) {
+  doNotOptimizeAway(globalInt64.readFast());
+}
+BENCHMARK(Sharded_readFast) {
+  doNotOptimizeAway(shd_int64.readFast());
+}
+BENCHMARK_DRAW_LINE();
+
+// multi threaded
+REG_BASELINE(Atomic_readFull,
+      doNotOptimizeAway(globalInt64Baseline.load(std::memory_order_relaxed)));
+REG_BASELINE(ThrCache_readFull, doNotOptimizeAway(globalInt64.readFull()));
+REG_BASELINE(Sharded_readFull, doNotOptimizeAway(shd_int64.readFull()));
+REG_BASELINE(ThrCache_readFast, doNotOptimizeAway(globalInt64.readFast()));
+REG_BASELINE(Sharded_readFast, doNotOptimizeAway(shd_int64.readFast()));
+BENCHMARK_DRAW_LINE();
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  gflags::SetCommandLineOptionWithMode(
+    "bm_min_usec", "10000", gflags::SET_FLAG_IF_DEFAULT
+  );
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return RUN_ALL_TESTS();
+}
+
+/*
+ Ran with 20 threads on dual 12-core Xeon(R) X5650 @ 2.67GHz with 12-MB caches
+
+ Benchmark                               Iters   Total t    t/iter iter/sec
+ ------------------------------------------------------------------------------
+ + 103% BM_mt_baseline__thread64     10000000  13.54 ms  1.354 ns  704.4 M
+*       BM_mt_baseline__thread32     10000000  6.651 ms  665.1 ps    1.4 G
+ +50.3% BM_mt_baseline_ThreadLocal64  10000000  9.994 ms  999.4 ps  954.2 M
+ +49.9% BM_mt_baseline_ThreadLocal32  10000000  9.972 ms  997.2 ps  956.4 M
+ +2650% BM_mt_baseline_atomic_inc64  10000000  182.9 ms  18.29 ns  52.13 M
+ +2665% BM_mt_baseline_atomic_inc32  10000000  183.9 ms  18.39 ns  51.85 M
+ +75.3% BM_mt_baseline_ShardedAtm64  10000000  11.66 ms  1.166 ns  817.8 M
+ +6670% BM_mt_cache_size64/0         10000000  450.3 ms  45.03 ns  21.18 M
+ +1644% BM_mt_cache_size64/10        10000000    116 ms   11.6 ns   82.2 M
+ + 381% BM_mt_cache_size64/100       10000000  32.04 ms  3.204 ns  297.7 M
+ + 129% BM_mt_cache_size64/1000      10000000  15.24 ms  1.524 ns  625.8 M
+ +6052% BM_mt_cache_size32/0         10000000  409.2 ms  40.92 ns  23.31 M
+ +1304% BM_mt_cache_size32/10        10000000  93.39 ms  9.339 ns  102.1 M
+ + 298% BM_mt_cache_size32/100       10000000  26.52 ms  2.651 ns  359.7 M
+ +68.1% BM_mt_cache_size32/1000      10000000  11.18 ms  1.118 ns  852.9 M
+------------------------------------------------------------------------------
+ +10.4% Atomic_readFull              10000000  36.05 ms  3.605 ns  264.5 M
+ + 619% ThrCache_readFull            10000000  235.1 ms  23.51 ns  40.57 M
+ SLOW   Sharded_readFull              1981093      2 s    1.01 us  967.3 k
+*       ThrCache_readFast            10000000  32.65 ms  3.265 ns  292.1 M
+ +10.0% Sharded_readFast             10000000  35.92 ms  3.592 ns  265.5 M
+------------------------------------------------------------------------------
+ +4.54% BM_mt_baseline_Atomic_readFull  10000000  8.672 ms  867.2 ps  1.074 G
+ SLOW   BM_mt_baseline_ThrCache_readFull  10000000  996.9 ms  99.69 ns  9.567 M
+ SLOW   BM_mt_baseline_Sharded_readFull  10000000  891.5 ms  89.15 ns   10.7 M
+*       BM_mt_baseline_ThrCache_readFast  10000000  8.295 ms  829.5 ps  1.123 G
+ +12.7% BM_mt_baseline_Sharded_readFast  10000000  9.348 ms  934.8 ps   1020 M
+------------------------------------------------------------------------------
+*/
diff --git a/faux-folly/folly/test/ThreadLocalTest.cpp b/faux-folly/folly/test/ThreadLocalTest.cpp
new file mode 100644
index 0000000..b6ab9cd
--- /dev/null
+++ b/faux-folly/folly/test/ThreadLocalTest.cpp
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2015 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/ThreadLocal.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <array>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <set>
+#include <thread>
+#include <unordered_map>
+
+#include <boost/thread/tss.hpp>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+
+using namespace folly;
+
+struct Widget {
+  static int totalVal_;
+  int val_;
+  ~Widget() {
+    totalVal_ += val_;
+  }
+
+  static void customDeleter(Widget* w, TLPDestructionMode mode) {
+    totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) * 1000;
+    delete w;
+  }
+};
+int Widget::totalVal_ = 0;
+
+TEST(ThreadLocalPtr, BasicDestructor) {
+  Widget::totalVal_ = 0;
+  ThreadLocalPtr<Widget> w;
+  std::thread([&w]() {
+      w.reset(new Widget());
+      w.get()->val_ += 10;
+    }).join();
+  EXPECT_EQ(10, Widget::totalVal_);
+}
+
+TEST(ThreadLocalPtr, CustomDeleter1) {
+  Widget::totalVal_ = 0;
+  {
+    ThreadLocalPtr<Widget> w;
+    std::thread([&w]() {
+        w.reset(new Widget(), Widget::customDeleter);
+        w.get()->val_ += 10;
+      }).join();
+    EXPECT_EQ(10, Widget::totalVal_);
+  }
+  EXPECT_EQ(10, Widget::totalVal_);
+}
+
+TEST(ThreadLocalPtr, resetNull) {
+  ThreadLocalPtr<int> tl;
+  EXPECT_FALSE(tl);
+  tl.reset(new int(4));
+  EXPECT_TRUE(static_cast<bool>(tl));
+  EXPECT_EQ(*tl.get(), 4);
+  tl.reset();
+  EXPECT_FALSE(tl);
+}
+
+TEST(ThreadLocalPtr, TestRelease) {
+  Widget::totalVal_ = 0;
+  ThreadLocalPtr<Widget> w;
+  std::unique_ptr<Widget> wPtr;
+  std::thread([&w, &wPtr]() {
+      w.reset(new Widget());
+      w.get()->val_ += 10;
+
+      wPtr.reset(w.release());
+    }).join();
+  EXPECT_EQ(0, Widget::totalVal_);
+  wPtr.reset();
+  EXPECT_EQ(10, Widget::totalVal_);
+}
+
+TEST(ThreadLocalPtr, CreateOnThreadExit) {
+  Widget::totalVal_ = 0;
+  ThreadLocal<Widget> w;
+  ThreadLocalPtr<int> tl;
+
+  std::thread([&] {
+      tl.reset(new int(1), [&] (int* ptr, TLPDestructionMode mode) {
+        delete ptr;
+        // This test ensures Widgets allocated here are not leaked.
+        ++w.get()->val_;
+        ThreadLocal<Widget> wl;
+        ++wl.get()->val_;
+      });
+    }).join();
+  EXPECT_EQ(2, Widget::totalVal_);
+}
+
+// Test deleting the ThreadLocalPtr object
+TEST(ThreadLocalPtr, CustomDeleter2) {
+  Widget::totalVal_ = 0;
+  std::thread t;
+  std::mutex mutex;
+  std::condition_variable cv;
+  enum class State {
+    START,
+    DONE,
+    EXIT
+  };
+  State state = State::START;
+  {
+    ThreadLocalPtr<Widget> w;
+    t = std::thread([&]() {
+        w.reset(new Widget(), Widget::customDeleter);
+        w.get()->val_ += 10;
+
+        // Notify main thread that we're done
+        {
+          std::unique_lock<std::mutex> lock(mutex);
+          state = State::DONE;
+          cv.notify_all();
+        }
+
+        // Wait for main thread to allow us to exit
+        {
+          std::unique_lock<std::mutex> lock(mutex);
+          while (state != State::EXIT) {
+            cv.wait(lock);
+          }
+        }
+    });
+
+    // Wait for main thread to start (and set w.get()->val_)
+    {
+      std::unique_lock<std::mutex> lock(mutex);
+      while (state != State::DONE) {
+        cv.wait(lock);
+      }
+    }
+
+    // Thread started but hasn't exited yet
+    EXPECT_EQ(0, Widget::totalVal_);
+
+    // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
+  }
+
+  EXPECT_EQ(1010, Widget::totalVal_);
+
+  // Allow thread to exit
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    state = State::EXIT;
+    cv.notify_all();
+  }
+  t.join();
+
+  EXPECT_EQ(1010, Widget::totalVal_);
+}
+
+TEST(ThreadLocal, BasicDestructor) {
+  Widget::totalVal_ = 0;
+  ThreadLocal<Widget> w;
+  std::thread([&w]() { w->val_ += 10; }).join();
+  EXPECT_EQ(10, Widget::totalVal_);
+}
+
+TEST(ThreadLocal, SimpleRepeatDestructor) {
+  Widget::totalVal_ = 0;
+  {
+    ThreadLocal<Widget> w;
+    w->val_ += 10;
+  }
+  {
+    ThreadLocal<Widget> w;
+    w->val_ += 10;
+  }
+  EXPECT_EQ(20, Widget::totalVal_);
+}
+
+TEST(ThreadLocal, InterleavedDestructors) {
+  Widget::totalVal_ = 0;
+  std::unique_ptr<ThreadLocal<Widget>> w;
+  int wVersion = 0;
+  const int wVersionMax = 2;
+  int thIter = 0;
+  std::mutex lock;
+  auto th = std::thread([&]() {
+    int wVersionPrev = 0;
+    while (true) {
+      while (true) {
+        std::lock_guard<std::mutex> g(lock);
+        if (wVersion > wVersionMax) {
+          return;
+        }
+        if (wVersion > wVersionPrev) {
+          // We have a new version of w, so it should be initialized to zero
+          EXPECT_EQ((*w)->val_, 0);
+          break;
+        }
+      }
+      std::lock_guard<std::mutex> g(lock);
+      wVersionPrev = wVersion;
+      (*w)->val_ += 10;
+      ++thIter;
+    }
+  });
+  FOR_EACH_RANGE(i, 0, wVersionMax) {
+    int thIterPrev = 0;
+    {
+      std::lock_guard<std::mutex> g(lock);
+      thIterPrev = thIter;
+      w.reset(new ThreadLocal<Widget>());
+      ++wVersion;
+    }
+    while (true) {
+      std::lock_guard<std::mutex> g(lock);
+      if (thIter > thIterPrev) {
+        break;
+      }
+    }
+  }
+  {
+    std::lock_guard<std::mutex> g(lock);
+    wVersion = wVersionMax + 1;
+  }
+  th.join();
+  EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
+}
+
+class SimpleThreadCachedInt {
+
+  class NewTag;
+  ThreadLocal<int,NewTag> val_;
+
+ public:
+  void add(int val) {
+    *val_ += val;
+  }
+
+  int read() {
+    int ret = 0;
+    for (const auto& i : val_.accessAllThreads()) {
+      ret += i;
+    }
+    return ret;
+  }
+};
+
+TEST(ThreadLocalPtr, AccessAllThreadsCounter) {
+  const int kNumThreads = 10;
+  SimpleThreadCachedInt stci;
+  std::atomic<bool> run(true);
+  std::atomic<int> totalAtomic(0);
+  std::vector<std::thread> threads;
+  for (int i = 0; i < kNumThreads; ++i) {
+    threads.push_back(std::thread([&,i]() {
+      stci.add(1);
+      totalAtomic.fetch_add(1);
+      while (run.load()) { usleep(100); }
+    }));
+  }
+  while (totalAtomic.load() != kNumThreads) { usleep(100); }
+  EXPECT_EQ(kNumThreads, stci.read());
+  run.store(false);
+  for (auto& t : threads) {
+    t.join();
+  }
+}
+
+TEST(ThreadLocal, resetNull) {
+  ThreadLocal<int> tl;
+  tl.reset(new int(4));
+  EXPECT_EQ(*tl.get(), 4);
+  tl.reset();
+  EXPECT_EQ(*tl.get(), 0);
+  tl.reset(new int(5));
+  EXPECT_EQ(*tl.get(), 5);
+}
+
+namespace {
+struct Tag {};
+
+struct Foo {
+  folly::ThreadLocal<int, Tag> tl;
+};
+}  // namespace
+
+TEST(ThreadLocal, Movable1) {
+  Foo a;
+  Foo b;
+  EXPECT_TRUE(a.tl.get() != b.tl.get());
+
+  a = Foo();
+  b = Foo();
+  EXPECT_TRUE(a.tl.get() != b.tl.get());
+}
+
+TEST(ThreadLocal, Movable2) {
+  std::map<int, Foo> map;
+
+  map[42];
+  map[10];
+  map[23];
+  map[100];
+
+  std::set<void*> tls;
+  for (auto& m : map) {
+    tls.insert(m.second.tl.get());
+  }
+
+  // Make sure that we have 4 different instances of *tl
+  EXPECT_EQ(4, tls.size());
+}
+
+namespace {
+
+constexpr size_t kFillObjectSize = 300;
+
+std::atomic<uint64_t> gDestroyed;
+
+/**
+ * Fill a chunk of memory with a unique-ish pattern that includes the thread id
+ * (so deleting one of these from another thread would cause a failure)
+ *
+ * Verify it explicitly and on destruction.
+ */
+class FillObject {
+ public:
+  explicit FillObject(uint64_t idx) : idx_(idx) {
+    uint64_t v = val();
+    for (size_t i = 0; i < kFillObjectSize; ++i) {
+      data_[i] = v;
+    }
+  }
+
+  void check() {
+    uint64_t v = val();
+    for (size_t i = 0; i < kFillObjectSize; ++i) {
+      CHECK_EQ(v, data_[i]);
+    }
+  }
+
+  ~FillObject() {
+    ++gDestroyed;
+  }
+
+ private:
+  uint64_t val() const {
+    return (idx_ << 40) | uint64_t(pthread_self());
+  }
+
+  uint64_t idx_;
+  uint64_t data_[kFillObjectSize];
+};
+
+}  // namespace
+
+#if FOLLY_HAVE_STD_THIS_THREAD_SLEEP_FOR
+TEST(ThreadLocal, Stress) {
+  constexpr size_t numFillObjects = 250;
+  std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
+
+  constexpr size_t numThreads = 32;
+  constexpr size_t numReps = 20;
+
+  std::vector<std::thread> threads;
+  threads.reserve(numThreads);
+
+  for (size_t i = 0; i < numThreads; ++i) {
+    threads.emplace_back([&objects] {
+      for (size_t rep = 0; rep < numReps; ++rep) {
+        for (size_t i = 0; i < objects.size(); ++i) {
+          objects[i].reset(new FillObject(rep * objects.size() + i));
+          std::this_thread::sleep_for(std::chrono::microseconds(100));
+        }
+        for (size_t i = 0; i < objects.size(); ++i) {
+          objects[i]->check();
+        }
+      }
+    });
+  }
+
+  for (auto& t : threads) {
+    t.join();
+  }
+
+  EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
+}
+#endif
+
+// Yes, threads and fork don't mix
+// (http://cppwisdom.quora.com/Why-threads-and-fork-dont-mix) but if you're
+// stupid or desperate enough to try, we shouldn't stand in your way.
+namespace {
+class HoldsOne {
+ public:
+  HoldsOne() : value_(1) { }
+  // Do an actual access to catch the buggy case where this == nullptr
+  int value() const { return value_; }
+ private:
+  int value_;
+};
+
+struct HoldsOneTag {};
+
+ThreadLocal<HoldsOne, HoldsOneTag> ptr;
+
+int totalValue() {
+  int value = 0;
+  for (auto& p : ptr.accessAllThreads()) {
+    value += p.value();
+  }
+  return value;
+}
+
+}  // namespace
+
+#ifdef FOLLY_HAVE_PTHREAD_ATFORK
+TEST(ThreadLocal, Fork) {
+  EXPECT_EQ(1, ptr->value());  // ensure created
+  EXPECT_EQ(1, totalValue());
+  // Spawn a new thread
+
+  std::mutex mutex;
+  bool started = false;
+  std::condition_variable startedCond;
+  bool stopped = false;
+  std::condition_variable stoppedCond;
+
+  std::thread t([&] () {
+    EXPECT_EQ(1, ptr->value());  // ensure created
+    {
+      std::unique_lock<std::mutex> lock(mutex);
+      started = true;
+      startedCond.notify_all();
+    }
+    {
+      std::unique_lock<std::mutex> lock(mutex);
+      while (!stopped) {
+        stoppedCond.wait(lock);
+      }
+    }
+  });
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    while (!started) {
+      startedCond.wait(lock);
+    }
+  }
+
+  EXPECT_EQ(2, totalValue());
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    // in child
+    int v = totalValue();
+
+    // exit successfully if v == 1 (one thread)
+    // diagnostic error code otherwise :)
+    switch (v) {
+    case 1: _exit(0);
+    case 0: _exit(1);
+    }
+    _exit(2);
+  } else if (pid > 0) {
+    // in parent
+    int status;
+    EXPECT_EQ(pid, waitpid(pid, &status, 0));
+    EXPECT_TRUE(WIFEXITED(status));
+    EXPECT_EQ(0, WEXITSTATUS(status));
+  } else {
+    EXPECT_TRUE(false) << "fork failed";
+  }
+
+  EXPECT_EQ(2, totalValue());
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    stopped = true;
+    stoppedCond.notify_all();
+  }
+
+  t.join();
+
+  EXPECT_EQ(1, totalValue());
+}
+#endif
+
+struct HoldsOneTag2 {};
+
+TEST(ThreadLocal, Fork2) {
+  // A thread-local tag that was used in the parent from a *different* thread
+  // (but not the forking thread) would cause the child to hang in a
+  // ThreadLocalPtr's object destructor. Yeah.
+  ThreadLocal<HoldsOne, HoldsOneTag2> p;
+  {
+    // use tag in different thread
+    std::thread t([&p] { p.get(); });
+    t.join();
+  }
+  pid_t pid = fork();
+  if (pid == 0) {
+    {
+      ThreadLocal<HoldsOne, HoldsOneTag2> q;
+      q.get();
+    }
+    _exit(0);
+  } else if (pid > 0) {
+    int status;
+    EXPECT_EQ(pid, waitpid(pid, &status, 0));
+    EXPECT_TRUE(WIFEXITED(status));
+    EXPECT_EQ(0, WEXITSTATUS(status));
+  } else {
+    EXPECT_TRUE(false) << "fork failed";
+  }
+}
+
+// Simple reference implementation using pthread_get_specific
+template<typename T>
+class PThreadGetSpecific {
+ public:
+  PThreadGetSpecific() : key_(0) {
+    pthread_key_create(&key_, OnThreadExit);
+  }
+
+  T* get() const {
+    return static_cast<T*>(pthread_getspecific(key_));
+  }
+
+  void reset(T* t) {
+    delete get();
+    pthread_setspecific(key_, t);
+  }
+  static void OnThreadExit(void* obj) {
+    delete static_cast<T*>(obj);
+  }
+ private:
+  pthread_key_t key_;
+};
+
+DEFINE_int32(numThreads, 8, "Number simultaneous threads for benchmarks.");
+
+#define REG(var)                                                \
+  BENCHMARK(FB_CONCATENATE(BM_mt_, var), iters) {               \
+    const int itersPerThread = iters / FLAGS_numThreads;        \
+    std::vector<std::thread> threads;                           \
+    for (int i = 0; i < FLAGS_numThreads; ++i) {                \
+      threads.push_back(std::thread([&]() {                     \
+        var.reset(new int(0));                                  \
+        for (int i = 0; i < itersPerThread; ++i) {              \
+          ++(*var.get());                                       \
+        }                                                       \
+      }));                                                      \
+    }                                                           \
+    for (auto& t : threads) {                                   \
+      t.join();                                                 \
+    }                                                           \
+  }
+
+ThreadLocalPtr<int> tlp;
+REG(tlp);
+PThreadGetSpecific<int> pthread_get_specific;
+REG(pthread_get_specific);
+boost::thread_specific_ptr<int> boost_tsp;
+REG(boost_tsp);
+BENCHMARK_DRAW_LINE();
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  gflags::SetCommandLineOptionWithMode(
+    "bm_max_iters", "100000000", gflags::SET_FLAG_IF_DEFAULT
+  );
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return RUN_ALL_TESTS();
+}
+
+/*
+Ran with 24 threads on dual 12-core Xeon(R) X5650 @ 2.67GHz with 12-MB caches
+
+Benchmark                               Iters   Total t    t/iter iter/sec
+------------------------------------------------------------------------------
+*       BM_mt_tlp                   100000000  39.88 ms  398.8 ps  2.335 G
+ +5.91% BM_mt_pthread_get_specific  100000000  42.23 ms  422.3 ps  2.205 G
+ + 295% BM_mt_boost_tsp             100000000  157.8 ms  1.578 ns  604.5 M
+------------------------------------------------------------------------------
+*/
diff --git a/faux-folly/folly/test/TimeseriesBenchmark.cpp b/faux-folly/folly/test/TimeseriesBenchmark.cpp
new file mode 100644
index 0000000..7145d3f
--- /dev/null
+++ b/faux-folly/folly/test/TimeseriesBenchmark.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 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/stats/BucketedTimeSeries.h>
+#include <folly/stats/BucketedTimeSeries-defs.h>
+
+#include <glog/logging.h>
+
+#include <folly/Benchmark.h>
+
+using std::chrono::seconds;
+using folly::BenchmarkSuspender;
+using folly::BucketedTimeSeries;
+
+void addValue(unsigned int iters,
+              seconds duration, size_t numBuckets,
+              size_t callsPerSecond) {
+  BenchmarkSuspender suspend;
+  BucketedTimeSeries<int64_t> ts(numBuckets, duration);
+  suspend.dismiss();
+
+  seconds currentTime(1342000000);
+  size_t timeCounter = 0;
+  for (unsigned int n = 0; n < iters; ++n, ++timeCounter) {
+    if (timeCounter >= callsPerSecond) {
+      timeCounter = 0;
+      ++currentTime;
+    }
+    ts.addValue(currentTime, n);
+  }
+}
+
+BENCHMARK_NAMED_PARAM(addValue, AllTime_1perSec, seconds(0), 60, 1);
+BENCHMARK_NAMED_PARAM(addValue, 3600x60_1perSec, seconds(3600), 60, 1);
+BENCHMARK_NAMED_PARAM(addValue, 600x60_1perSec, seconds(600), 60, 1);
+BENCHMARK_NAMED_PARAM(addValue, 60x60_1perSec, seconds(60), 60, 1);
+BENCHMARK_NAMED_PARAM(addValue, 100x10_1perSec, seconds(100), 10, 1);
+BENCHMARK_NAMED_PARAM(addValue, 71x5_1perSec, seconds(71), 5, 1);
+BENCHMARK_NAMED_PARAM(addValue, 1x1_1perSec, seconds(1), 1, 1);
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK_NAMED_PARAM(addValue, AllTime_10perSec, seconds(0), 60, 10);
+BENCHMARK_NAMED_PARAM(addValue, 3600x60_10perSec, seconds(3600), 60, 10);
+BENCHMARK_NAMED_PARAM(addValue, 600x60_10perSec, seconds(600), 60, 10);
+BENCHMARK_NAMED_PARAM(addValue, 60x60_10perSec, seconds(60), 60, 10);
+BENCHMARK_NAMED_PARAM(addValue, 100x10_10perSec, seconds(100), 10, 10);
+BENCHMARK_NAMED_PARAM(addValue, 71x5_10perSec, seconds(71), 5, 10);
+BENCHMARK_NAMED_PARAM(addValue, 1x1_10perSec, seconds(1), 1, 10);
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK_NAMED_PARAM(addValue, AllTime_100perSec, seconds(0), 60, 100);
+BENCHMARK_NAMED_PARAM(addValue, 3600x60_100perSec, seconds(3600), 60, 100);
+BENCHMARK_NAMED_PARAM(addValue, 600x60_100perSec, seconds(600), 60, 100);
+BENCHMARK_NAMED_PARAM(addValue, 60x60_100perSec, seconds(60), 60, 100);
+BENCHMARK_NAMED_PARAM(addValue, 100x10_100perSec, seconds(100), 10, 100);
+BENCHMARK_NAMED_PARAM(addValue, 71x5_100perSec, seconds(71), 5, 100);
+BENCHMARK_NAMED_PARAM(addValue, 1x1_100perSec, seconds(1), 1, 100);
+
+int main(int argc, char *argv[]) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+  return 0;
+}
diff --git a/faux-folly/folly/test/TimeseriesHistogramTest.cpp b/faux-folly/folly/test/TimeseriesHistogramTest.cpp
new file mode 100644
index 0000000..85e3198
--- /dev/null
+++ b/faux-folly/folly/test/TimeseriesHistogramTest.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2015 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/stats/TimeseriesHistogram.h>
+#include <folly/stats/TimeseriesHistogram-defs.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace folly;
+using std::chrono::seconds;
+
+namespace IntMTMHTS {
+  enum Levels {
+    MINUTE,
+    TEN_MINUTE,
+    HOUR,
+    ALLTIME,
+    NUM_LEVELS,
+  };
+
+  const seconds kDurations[] = {
+    seconds(60), seconds(600), seconds(3600), seconds(0)
+  };
+};
+
+namespace IntMHTS {
+  enum Levels {
+    MINUTE,
+    HOUR,
+    ALLTIME,
+    NUM_LEVELS,
+  };
+
+  const seconds kDurations[] = {
+    seconds(60), seconds(3600), seconds(0)
+  };
+};
+
+typedef std::mt19937 RandomInt32;
+
+TEST(TimeseriesHistogram, Percentile) {
+  RandomInt32 random(5);
+  // [10, 109], 12 buckets including above and below
+  {
+    TimeseriesHistogram<int> h(10, 10, 110,
+                               MultiLevelTimeSeries<int>(
+                                 60, IntMTMHTS::NUM_LEVELS,
+                                 IntMTMHTS::kDurations));
+
+    EXPECT_EQ(0, h.getPercentileEstimate(0, IntMTMHTS::ALLTIME));
+
+    EXPECT_EQ(12, h.getNumBuckets());
+    EXPECT_EQ(10, h.getBucketSize());
+    EXPECT_EQ(10, h.getMin());
+    EXPECT_EQ(110, h.getMax());
+
+    for (int i = 0; i < h.getNumBuckets(); ++i) {
+      EXPECT_EQ(4, h.getBucket(i).numLevels());
+    }
+
+    int maxVal = 120;
+    h.addValue(seconds(0), 0);
+    h.addValue(seconds(0), maxVal);
+    for (int i = 0; i < 98; i++) {
+      h.addValue(seconds(0), random() % maxVal);
+    }
+
+    h.update(std::chrono::duration_cast<std::chrono::seconds>(
+               std::chrono::system_clock::now().time_since_epoch()));
+    // bucket 0 stores everything below min, so its minimum
+    // is the lowest possible number
+    EXPECT_EQ(std::numeric_limits<int>::min(),
+              h.getPercentileBucketMin(1, IntMTMHTS::ALLTIME));
+    EXPECT_EQ(110, h.getPercentileBucketMin(99, IntMTMHTS::ALLTIME));
+
+    EXPECT_EQ(-2, h.getPercentileEstimate(0, IntMTMHTS::ALLTIME));
+    EXPECT_EQ(-1, h.getPercentileEstimate(1, IntMTMHTS::ALLTIME));
+    EXPECT_EQ(119, h.getPercentileEstimate(99, IntMTMHTS::ALLTIME));
+    EXPECT_EQ(120, h.getPercentileEstimate(100, IntMTMHTS::ALLTIME));
+  }
+}
+
+TEST(TimeseriesHistogram, String) {
+  RandomInt32 random(5);
+  // [10, 109], 12 buckets including above and below
+  {
+    TimeseriesHistogram<int> hist(10, 10, 110,
+                                  MultiLevelTimeSeries<int>(
+                                    60, IntMTMHTS::NUM_LEVELS,
+                                    IntMTMHTS::kDurations));
+
+    int maxVal = 120;
+    hist.addValue(seconds(0), 0);
+    hist.addValue(seconds(0), maxVal);
+    for (int i = 0; i < 98; i++) {
+      hist.addValue(seconds(0), random() % maxVal);
+    }
+
+    hist.update(seconds(0));
+
+    const char* const kStringValues1[IntMTMHTS::NUM_LEVELS] =  {
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+    };
+
+    CHECK_EQ(IntMTMHTS::NUM_LEVELS, hist.getNumLevels());
+
+    for (int level = 0; level < hist.getNumLevels(); ++level) {
+      EXPECT_EQ(kStringValues1[level], hist.getString(level));
+    }
+
+    const char* const kStringValues2[IntMTMHTS::NUM_LEVELS] =  {
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+      "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64,"
+        "70:7:74,80:8:84,90:10:94,100:3:103,110:10:115",
+    };
+
+    CHECK_EQ(IntMTMHTS::NUM_LEVELS, hist.getNumLevels());
+
+    for (int level = 0; level < hist.getNumLevels(); ++level) {
+      EXPECT_EQ(kStringValues2[level], hist.getString(level));
+    }
+  }
+}
+
+TEST(TimeseriesHistogram, Clear) {
+  {
+    TimeseriesHistogram<int> hist(10, 0, 100,
+                                  MultiLevelTimeSeries<int>(
+                                    60, IntMTMHTS::NUM_LEVELS,
+                                    IntMTMHTS::kDurations));
+
+    for (int now = 0; now < 3600; now++) {
+      for (int i = 0; i < 100; i++) {
+        hist.addValue(seconds(now), i, 2);  // adds each item 2 times
+      }
+    }
+
+    // check clearing
+    hist.clear();
+
+    for (int b = 0; b < hist.getNumBuckets(); ++b) {
+      EXPECT_EQ(0, hist.getBucket(b).count(IntMTMHTS::MINUTE));
+      EXPECT_EQ(0, hist.getBucket(b).count(IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(0, hist.getBucket(b).count(IntMTMHTS::HOUR));
+      EXPECT_EQ(0, hist.getBucket(b).count(IntMTMHTS::ALLTIME));
+    }
+
+    for (int pct = 0; pct <= 100; pct++) {
+      EXPECT_EQ(0, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE));
+      EXPECT_EQ(0, hist.getPercentileBucketMin(pct, IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(0, hist.getPercentileBucketMin(pct, IntMTMHTS::HOUR));
+      EXPECT_EQ(0, hist.getPercentileBucketMin(pct, IntMTMHTS::ALLTIME));
+
+      EXPECT_EQ(0, hist.getPercentileEstimate(pct, IntMTMHTS::MINUTE));
+      EXPECT_EQ(0, hist.getPercentileEstimate(pct, IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(0, hist.getPercentileEstimate(pct, IntMTMHTS::HOUR));
+      EXPECT_EQ(0, hist.getPercentileEstimate(pct, IntMTMHTS::ALLTIME));
+    }
+  }
+}
+
+
+TEST(TimeseriesHistogram, Basic) {
+  {
+    TimeseriesHistogram<int> hist(10, 0, 100,
+                                  MultiLevelTimeSeries<int>(
+                                    60, IntMTMHTS::NUM_LEVELS,
+                                    IntMTMHTS::kDurations));
+
+    for (int now = 0; now < 3600; now++) {
+      for (int i = 0; i < 100; i++) {
+        hist.addValue(seconds(now), i);
+      }
+    }
+
+    hist.update(seconds(3599));
+    for (int pct = 1; pct <= 100; pct++) {
+      int expected = (pct - 1) / 10 * 10;
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct,
+                                                      IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::HOUR));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::ALLTIME));
+    }
+
+    for (int b = 1; (b + 1) < hist.getNumBuckets(); ++b) {
+      EXPECT_EQ(600, hist.getBucket(b).count(IntMTMHTS::MINUTE));
+      EXPECT_EQ(6000, hist.getBucket(b).count(IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(36000, hist.getBucket(b).count(IntMTMHTS::HOUR));
+      EXPECT_EQ(36000, hist.getBucket(b).count(IntMTMHTS::ALLTIME));
+    }
+    EXPECT_EQ(0, hist.getBucket(0).count(IntMTMHTS::MINUTE));
+    EXPECT_EQ(0, hist.getBucket(hist.getNumBuckets() - 1).count(
+                IntMTMHTS::MINUTE));
+  }
+
+  // -----------------
+
+  {
+    TimeseriesHistogram<int> hist(10, 0, 100,
+                                  MultiLevelTimeSeries<int>(
+                                    60, IntMTMHTS::NUM_LEVELS,
+                                    IntMTMHTS::kDurations));
+
+    for (int now = 0; now < 3600; now++) {
+      for (int i = 0; i < 100; i++) {
+        hist.addValue(seconds(now), i, 2);  // adds each item 2 times
+      }
+    }
+
+    hist.update(seconds(3599));
+    for (int pct = 1; pct <= 100; pct++) {
+      int expected = (pct - 1) / 10 * 10;
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct,
+                                                      IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::HOUR));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::ALLTIME));
+   }
+
+    for (int b = 1; (b + 1) < hist.getNumBuckets(); ++b) {
+      EXPECT_EQ(600 * 2, hist.getBucket(b).count(IntMTMHTS::MINUTE));
+      EXPECT_EQ(6000 * 2, hist.getBucket(b).count(IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(36000 * 2, hist.getBucket(b).count(IntMTMHTS::HOUR));
+      EXPECT_EQ(36000 * 2, hist.getBucket(b).count(IntMTMHTS::ALLTIME));
+    }
+    EXPECT_EQ(0, hist.getBucket(0).count(IntMTMHTS::MINUTE));
+    EXPECT_EQ(0, hist.getBucket(hist.getNumBuckets() - 1).count(
+                IntMTMHTS::MINUTE));
+  }
+
+  // -----------------
+
+  {
+    TimeseriesHistogram<int> hist(10, 0, 100,
+                                  MultiLevelTimeSeries<int>(
+                                    60, IntMTMHTS::NUM_LEVELS,
+                                    IntMTMHTS::kDurations));
+
+    for (int now = 0; now < 3600; now++) {
+      for (int i = 0; i < 50; i++) {
+        hist.addValue(seconds(now), i * 2, 2);  // adds each item 2 times
+      }
+    }
+
+    hist.update(seconds(3599));
+    for (int pct = 1; pct <= 100; pct++) {
+      int expected = (pct - 1) / 10 * 10;
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct,
+                                                      IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::HOUR));
+      EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::ALLTIME));
+    }
+
+    EXPECT_EQ(0, hist.getBucket(0).count(IntMTMHTS::MINUTE));
+    EXPECT_EQ(0, hist.getBucket(0).count(IntMTMHTS::TEN_MINUTE));
+    EXPECT_EQ(0, hist.getBucket(0).count(IntMTMHTS::HOUR));
+    EXPECT_EQ(0, hist.getBucket(0).count(IntMTMHTS::ALLTIME));
+    EXPECT_EQ(0, hist.getBucket(hist.getNumBuckets() - 1).count(
+                IntMTMHTS::MINUTE));
+    EXPECT_EQ(0,
+              hist.getBucket(hist.getNumBuckets() - 1).
+                count(IntMTMHTS::TEN_MINUTE));
+    EXPECT_EQ(0, hist.getBucket(hist.getNumBuckets() - 1).count(
+                IntMTMHTS::HOUR));
+    EXPECT_EQ(0,
+              hist.getBucket(hist.getNumBuckets() - 1).count(
+                IntMTMHTS::ALLTIME));
+
+    for (int b = 1; (b + 1) < hist.getNumBuckets(); ++b) {
+      EXPECT_EQ(600, hist.getBucket(b).count(IntMTMHTS::MINUTE));
+      EXPECT_EQ(6000, hist.getBucket(b).count(IntMTMHTS::TEN_MINUTE));
+      EXPECT_EQ(36000, hist.getBucket(b).count(IntMTMHTS::HOUR));
+      EXPECT_EQ(36000, hist.getBucket(b).count(IntMTMHTS::ALLTIME));
+    }
+
+    for (int i = 0; i < 100; ++i) {
+      hist.addValue(seconds(3599), 200 + i);
+    }
+    hist.update(seconds(3599));
+    EXPECT_EQ(100,
+              hist.getBucket(hist.getNumBuckets() - 1).count(
+                IntMTMHTS::ALLTIME));
+
+  }
+}
+
+TEST(TimeseriesHistogram, QueryByInterval) {
+  TimeseriesHistogram<int> mhts(8, 8, 120,
+                                MultiLevelTimeSeries<int>(
+                                  60, IntMHTS::NUM_LEVELS,
+                                  IntMHTS::kDurations));
+
+  mhts.update(seconds(0));
+
+  int curTime;
+  for (curTime = 0; curTime < 7200; curTime++) {
+    mhts.addValue(seconds(curTime), 1);
+  }
+  for (curTime = 7200; curTime < 7200 + 3540; curTime++) {
+    mhts.addValue(seconds(curTime), 10);
+  }
+  for (curTime = 7200 + 3540; curTime < 7200 + 3600; curTime++) {
+    mhts.addValue(seconds(curTime), 100);
+  }
+
+  mhts.update(seconds(7200 + 3600 - 1));
+
+  struct TimeInterval {
+    TimeInterval(int s, int e)
+      : start(s), end(e) {}
+
+    std::chrono::seconds start;
+    std::chrono::seconds end;
+  };
+  TimeInterval intervals[12] = {
+    { curTime - 60, curTime },
+    { curTime - 3600, curTime },
+    { curTime - 7200, curTime },
+    { curTime - 3600, curTime - 60 },
+    { curTime - 7200, curTime - 60 },
+    { curTime - 7200, curTime - 3600 },
+    { curTime - 50, curTime - 20 },
+    { curTime - 3020, curTime - 20 },
+    { curTime - 7200, curTime - 20 },
+    { curTime - 3000, curTime - 1000 },
+    { curTime - 7200, curTime - 1000 },
+    { curTime - 7200, curTime - 3600 },
+  };
+
+  int expectedSums[12] = {
+    6000, 41400, 32400, 35400, 32129, 16200, 3000, 33600, 32308, 20000, 27899,
+    16200
+  };
+
+  int expectedCounts[12] = {
+    60, 3600, 7200, 3540, 7139, 3600, 30, 3000, 7178, 2000, 6199, 3600
+  };
+
+  // The first 7200 values added all fell below the histogram minimum,
+  // and went into the bucket that tracks all of the too-small values.
+  // This bucket reports a minimum value of the smallest possible integer.
+  int belowMinBucket = std::numeric_limits<int>::min();
+
+  int expectedValues[12][3] = {
+    {96, 96, 96},
+    { 8,  8, 96},
+    { belowMinBucket,  belowMinBucket,  8}, // alltime
+    { 8,  8,  8},
+    { belowMinBucket,  belowMinBucket,  8}, // alltime
+    { belowMinBucket,  belowMinBucket,  8}, // alltime
+    {96, 96, 96},
+    { 8,  8, 96},
+    { belowMinBucket,  belowMinBucket,  8}, // alltime
+    { 8,  8,  8},
+    { belowMinBucket,  belowMinBucket,  8}, // alltime
+    { belowMinBucket,  belowMinBucket,  8}  // alltime
+  };
+
+  for (int i = 0; i < 12; i++) {
+    const auto& itv = intervals[i];
+    int s = mhts.sum(itv.start, itv.end);
+    EXPECT_EQ(expectedSums[i], s);
+
+    int c = mhts.count(itv.start, itv.end);
+    EXPECT_EQ(expectedCounts[i], c);
+  }
+
+  // 3 levels
+  for (int i = 1; i <= 100; i++) {
+    EXPECT_EQ(96, mhts.getPercentileBucketMin(i, 0));
+    EXPECT_EQ(96, mhts.getPercentileBucketMin(i, seconds(curTime - 60),
+                                              seconds(curTime)));
+    EXPECT_EQ(8, mhts.getPercentileBucketMin(i, seconds(curTime - 3540),
+                                             seconds(curTime - 60)));
+  }
+
+  EXPECT_EQ(8, mhts.getPercentileBucketMin(1, 1));
+  EXPECT_EQ(8, mhts.getPercentileBucketMin(98, 1));
+  EXPECT_EQ(96, mhts.getPercentileBucketMin(99, 1));
+  EXPECT_EQ(96, mhts.getPercentileBucketMin(100, 1));
+
+  EXPECT_EQ(belowMinBucket, mhts.getPercentileBucketMin(1, 2));
+  EXPECT_EQ(belowMinBucket, mhts.getPercentileBucketMin(66, 2));
+  EXPECT_EQ(8, mhts.getPercentileBucketMin(67, 2));
+  EXPECT_EQ(8, mhts.getPercentileBucketMin(99, 2));
+  EXPECT_EQ(96, mhts.getPercentileBucketMin(100, 2));
+
+  // 0 is currently the value for bucket 0 (below min)
+  for (int i = 0; i < 12; i++) {
+    const auto& itv = intervals[i];
+    int v = mhts.getPercentileBucketMin(1, itv.start, itv.end);
+    EXPECT_EQ(expectedValues[i][0], v);
+
+    v = mhts.getPercentileBucketMin(50, itv.start, itv.end);
+    EXPECT_EQ(expectedValues[i][1], v);
+
+    v = mhts.getPercentileBucketMin(99, itv.start, itv.end);
+    EXPECT_EQ(expectedValues[i][2], v);
+  }
+
+  for (int i = 0; i < 12; i++) {
+    const auto& itv = intervals[i];
+    int c = mhts.count(itv.start, itv.end);
+    // Some of the older intervals that fall in the alltime bucket
+    // are off by 1 or 2 in their estimated counts.
+    size_t tolerance = 0;
+    if (itv.start <= seconds(curTime - 7200)) {
+      tolerance = 2;
+    } else if (itv.start <= seconds(curTime - 3000)) {
+      tolerance = 1;
+    }
+    size_t actualCount = (itv.end - itv.start).count();
+    size_t estimatedCount = mhts.count(itv.start, itv.end);
+    EXPECT_GE(actualCount, estimatedCount);
+    EXPECT_LE(actualCount - tolerance, estimatedCount);
+  }
+}
+
+TEST(TimeseriesHistogram, SingleUniqueValue) {
+  int values[] = {-1, 0, 500, 1000, 1500};
+  for (int ii = 0; ii < 5; ++ii) {
+    int value = values[ii];
+    TimeseriesHistogram<int> h(10, 0, 1000,
+                               MultiLevelTimeSeries<int>(
+                                 60, IntMTMHTS::NUM_LEVELS,
+                                 IntMTMHTS::kDurations));
+
+    const int kNumIters = 1000;
+    for (int jj = 0; jj < kNumIters; ++jj) {
+      h.addValue(seconds(time(nullptr)), value);
+    }
+    h.update(seconds(time(nullptr)));
+    // since we've only added one unique value, all percentiles should
+    // be that value
+    EXPECT_EQ(h.getPercentileEstimate(10, 0), value);
+    EXPECT_EQ(h.getPercentileEstimate(50, 0), value);
+    EXPECT_EQ(h.getPercentileEstimate(99, 0), value);
+
+    // Things get trickier if there are multiple unique values.
+    const int kNewValue = 750;
+    for (int kk = 0; kk < 2*kNumIters; ++kk) {
+      h.addValue(seconds(time(nullptr)), kNewValue);
+    }
+    h.update(seconds(time(nullptr)));
+    EXPECT_NEAR(h.getPercentileEstimate(50, 0), kNewValue+5, 5);
+    if (value >= 0 && value <= 1000) {
+      // only do further testing if value is within our bucket range,
+      // else estimates can be wildly off
+      if (kNewValue > value) {
+        EXPECT_NEAR(h.getPercentileEstimate(10, 0), value+5, 5);
+        EXPECT_NEAR(h.getPercentileEstimate(99, 0), kNewValue+5, 5);
+      } else {
+        EXPECT_NEAR(h.getPercentileEstimate(10, 0), kNewValue+5, 5);
+        EXPECT_NEAR(h.getPercentileEstimate(99, 0), value+5, 5);
+      }
+    }
+  }
+}
diff --git a/faux-folly/folly/test/TimeseriesTest.cpp b/faux-folly/folly/test/TimeseriesTest.cpp
new file mode 100644
index 0000000..e23d0b7
--- /dev/null
+++ b/faux-folly/folly/test/TimeseriesTest.cpp
@@ -0,0 +1,957 @@
+/*
+ * Copyright 2015 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/stats/BucketedTimeSeries.h>
+#include <folly/stats/BucketedTimeSeries-defs.h>
+#include <folly/stats/MultiLevelTimeSeries.h>
+#include <folly/stats/MultiLevelTimeSeries-defs.h>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+#include <folly/Foreach.h>
+
+using std::chrono::seconds;
+using std::string;
+using std::vector;
+using folly::BucketedTimeSeries;
+
+struct TestData {
+  size_t duration;
+  size_t numBuckets;
+  vector<ssize_t> bucketStarts;
+};
+vector<TestData> testData = {
+  // 71 seconds x 4 buckets
+  { 71, 4, {0, 18, 36, 54}},
+  // 100 seconds x 10 buckets
+  { 100, 10, {0, 10, 20, 30, 40, 50, 60, 70, 80, 90}},
+  // 10 seconds x 10 buckets
+  { 10, 10, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}},
+  // 10 seconds x 1 buckets
+  { 10, 1, {0}},
+  // 1 second x 1 buckets
+  { 1, 1, {0}},
+};
+
+TEST(BucketedTimeSeries, getBucketInfo) {
+  for (const auto& data : testData) {
+    BucketedTimeSeries<int64_t> ts(data.numBuckets, seconds(data.duration));
+
+    for (uint32_t n = 0; n < 10000; n += 1234) {
+      seconds offset(n * data.duration);
+
+      for (uint32_t idx = 0; idx < data.numBuckets; ++idx) {
+        seconds bucketStart(data.bucketStarts[idx]);
+        seconds nextBucketStart;
+        if (idx + 1 < data.numBuckets) {
+            nextBucketStart = seconds(data.bucketStarts[idx + 1]);
+        } else {
+            nextBucketStart = seconds(data.duration);
+        }
+
+        seconds expectedStart = offset + bucketStart;
+        seconds expectedNextStart = offset + nextBucketStart;
+        seconds midpoint = (expectedStart + expectedNextStart) / 2;
+
+        vector<std::pair<string, seconds>> timePoints = {
+          {"expectedStart", expectedStart},
+          {"midpoint", midpoint},
+          {"expectedEnd", expectedNextStart - seconds(1)},
+        };
+
+        for (const auto& point : timePoints) {
+          // Check that getBucketIdx() returns the expected index
+          EXPECT_EQ(idx, ts.getBucketIdx(point.second)) <<
+            data.duration << "x" << data.numBuckets << ": " <<
+            point.first << "=" << point.second.count();
+
+          // Check the data returned by getBucketInfo()
+          size_t returnedIdx;
+          seconds returnedStart;
+          seconds returnedNextStart;
+          ts.getBucketInfo(expectedStart, &returnedIdx,
+                           &returnedStart, &returnedNextStart);
+          EXPECT_EQ(idx, returnedIdx) <<
+            data.duration << "x" << data.numBuckets << ": " <<
+            point.first << "=" << point.second.count();
+          EXPECT_EQ(expectedStart.count(), returnedStart.count()) <<
+            data.duration << "x" << data.numBuckets << ": " <<
+            point.first << "=" << point.second.count();
+          EXPECT_EQ(expectedNextStart.count(), returnedNextStart.count()) <<
+            data.duration << "x" << data.numBuckets << ": " <<
+            point.first << "=" << point.second.count();
+        }
+      }
+    }
+  }
+}
+
+void testUpdate100x10(size_t offset) {
+  // This test code only works when offset is a multiple of the bucket width
+  CHECK_EQ(0, offset % 10);
+
+  // Create a 100 second timeseries, with 10 buckets
+  BucketedTimeSeries<int64_t> ts(10, seconds(100));
+
+  auto setup = [&] {
+    ts.clear();
+    // Add 1 value to each bucket
+    for (int n = 5; n <= 95; n += 10) {
+      ts.addValue(seconds(n + offset), 6);
+    }
+
+    EXPECT_EQ(10, ts.count());
+    EXPECT_EQ(60, ts.sum());
+    EXPECT_EQ(6, ts.avg());
+  };
+
+  // Update 2 buckets forwards.  This should throw away 2 data points.
+  setup();
+  ts.update(seconds(110 + offset));
+  EXPECT_EQ(8, ts.count());
+  EXPECT_EQ(48, ts.sum());
+  EXPECT_EQ(6, ts.avg());
+
+  // The last time we added was 95.
+  // Try updating to 189.  This should clear everything but the last bucket.
+  setup();
+  ts.update(seconds(151 + offset));
+  EXPECT_EQ(4, ts.count());
+  //EXPECT_EQ(6, ts.sum());
+  EXPECT_EQ(6, ts.avg());
+
+  // The last time we added was 95.
+  // Try updating to 193: This is nearly one full loop around,
+  // back to the same bucket.  update() needs to clear everything
+  setup();
+  ts.update(seconds(193 + offset));
+  EXPECT_EQ(0, ts.count());
+  EXPECT_EQ(0, ts.sum());
+  EXPECT_EQ(0, ts.avg());
+
+  // The last time we added was 95.
+  // Try updating to 197: This is slightly over one full loop around,
+  // back to the same bucket.  update() needs to clear everything
+  setup();
+  ts.update(seconds(197 + offset));
+  EXPECT_EQ(0, ts.count());
+  EXPECT_EQ(0, ts.sum());
+  EXPECT_EQ(0, ts.avg());
+
+  // The last time we added was 95.
+  // Try updating to 230: This is well over one full loop around,
+  // and everything should be cleared.
+  setup();
+  ts.update(seconds(230 + offset));
+  EXPECT_EQ(0, ts.count());
+  EXPECT_EQ(0, ts.sum());
+  EXPECT_EQ(0, ts.avg());
+}
+
+TEST(BucketedTimeSeries, update100x10) {
+  // Run testUpdate100x10() multiple times, with various offsets.
+  // This makes sure the update code works regardless of which bucket it starts
+  // at in the modulo arithmetic.
+  testUpdate100x10(0);
+  testUpdate100x10(50);
+  testUpdate100x10(370);
+  testUpdate100x10(1937090);
+}
+
+TEST(BucketedTimeSeries, update71x5) {
+  // Create a 71 second timeseries, with 5 buckets
+  // This tests when the number of buckets does not divide evenly into the
+  // duration.
+  BucketedTimeSeries<int64_t> ts(5, seconds(71));
+
+  auto setup = [&] {
+    ts.clear();
+    // Add 1 value to each bucket
+    ts.addValue(seconds(11), 6);
+    ts.addValue(seconds(24), 6);
+    ts.addValue(seconds(42), 6);
+    ts.addValue(seconds(43), 6);
+    ts.addValue(seconds(66), 6);
+
+    EXPECT_EQ(5, ts.count());
+    EXPECT_EQ(30, ts.sum());
+    EXPECT_EQ(6, ts.avg());
+  };
+
+  // Update 2 buckets forwards.  This should throw away 2 data points.
+  setup();
+  ts.update(seconds(99));
+  EXPECT_EQ(3, ts.count());
+  EXPECT_EQ(18, ts.sum());
+  EXPECT_EQ(6, ts.avg());
+
+  // Update 3 buckets forwards.  This should throw away 3 data points.
+  setup();
+  ts.update(seconds(100));
+  EXPECT_EQ(2, ts.count());
+  EXPECT_EQ(12, ts.sum());
+  EXPECT_EQ(6, ts.avg());
+
+  // Update 4 buckets forwards, just under the wrap limit.
+  // This should throw everything but the last bucket away.
+  setup();
+  ts.update(seconds(127));
+  EXPECT_EQ(1, ts.count());
+  EXPECT_EQ(6, ts.sum());
+  EXPECT_EQ(6, ts.avg());
+
+  // Update 5 buckets forwards, exactly at the wrap limit.
+  // This should throw everything away.
+  setup();
+  ts.update(seconds(128));
+  EXPECT_EQ(0, ts.count());
+  EXPECT_EQ(0, ts.sum());
+  EXPECT_EQ(0, ts.avg());
+
+  // Update very far forwards, wrapping multiple times.
+  // This should throw everything away.
+  setup();
+  ts.update(seconds(1234));
+  EXPECT_EQ(0, ts.count());
+  EXPECT_EQ(0, ts.sum());
+  EXPECT_EQ(0, ts.avg());
+}
+
+TEST(BucketedTimeSeries, elapsed) {
+  BucketedTimeSeries<int64_t> ts(60, seconds(600));
+
+  // elapsed() is 0 when no data points have been added
+  EXPECT_EQ(0, ts.elapsed().count());
+
+  // With exactly 1 data point, elapsed() should report 1 second of data
+  seconds start(239218);
+  ts.addValue(start + seconds(0), 200);
+  EXPECT_EQ(1, ts.elapsed().count());
+  // Adding a data point 10 seconds later should result in an elapsed time of
+  // 11 seconds (the time range is [0, 10], inclusive).
+  ts.addValue(start + seconds(10), 200);
+  EXPECT_EQ(11, ts.elapsed().count());
+
+  // elapsed() returns to 0 after clear()
+  ts.clear();
+  EXPECT_EQ(0, ts.elapsed().count());
+
+  // Restart, with the starting point on an easier number to work with
+  ts.addValue(seconds(10), 200);
+  EXPECT_EQ(1, ts.elapsed().count());
+  ts.addValue(seconds(580), 200);
+  EXPECT_EQ(571, ts.elapsed().count());
+  ts.addValue(seconds(590), 200);
+  EXPECT_EQ(581, ts.elapsed().count());
+  ts.addValue(seconds(598), 200);
+  EXPECT_EQ(589, ts.elapsed().count());
+  ts.addValue(seconds(599), 200);
+  EXPECT_EQ(590, ts.elapsed().count());
+  ts.addValue(seconds(600), 200);
+  EXPECT_EQ(591, ts.elapsed().count());
+  ts.addValue(seconds(608), 200);
+  EXPECT_EQ(599, ts.elapsed().count());
+  ts.addValue(seconds(609), 200);
+  EXPECT_EQ(600, ts.elapsed().count());
+  // Once we reach 600 seconds worth of data, when we move on to the next
+  // second a full bucket will get thrown out.  Now we drop back down to 591
+  // seconds worth of data
+  ts.addValue(seconds(610), 200);
+  EXPECT_EQ(591, ts.elapsed().count());
+  ts.addValue(seconds(618), 200);
+  EXPECT_EQ(599, ts.elapsed().count());
+  ts.addValue(seconds(619), 200);
+  EXPECT_EQ(600, ts.elapsed().count());
+  ts.addValue(seconds(620), 200);
+  EXPECT_EQ(591, ts.elapsed().count());
+  ts.addValue(seconds(123419), 200);
+  EXPECT_EQ(600, ts.elapsed().count());
+  ts.addValue(seconds(123420), 200);
+  EXPECT_EQ(591, ts.elapsed().count());
+  ts.addValue(seconds(123425), 200);
+  EXPECT_EQ(596, ts.elapsed().count());
+
+  // Time never moves backwards.
+  // Calling update with an old timestamp will just be ignored.
+  ts.update(seconds(29));
+  EXPECT_EQ(596, ts.elapsed().count());
+}
+
+TEST(BucketedTimeSeries, rate) {
+  BucketedTimeSeries<int64_t> ts(60, seconds(600));
+
+  // Add 3 values every 2 seconds, until fill up the buckets
+  for (size_t n = 0; n < 600; n += 2) {
+    ts.addValue(seconds(n), 200, 3);
+  }
+
+  EXPECT_EQ(900, ts.count());
+  EXPECT_EQ(180000, ts.sum());
+  EXPECT_EQ(200, ts.avg());
+
+  // Really we only entered 599 seconds worth of data: [0, 598] (inclusive)
+  EXPECT_EQ(599, ts.elapsed().count());
+  EXPECT_NEAR(300.5, ts.rate(), 0.005);
+  EXPECT_NEAR(1.5, ts.countRate(), 0.005);
+
+  // If we add 1 more second, now we will have 600 seconds worth of data
+  ts.update(seconds(599));
+  EXPECT_EQ(600, ts.elapsed().count());
+  EXPECT_NEAR(300, ts.rate(), 0.005);
+  EXPECT_EQ(300, ts.rate<int>());
+  EXPECT_NEAR(1.5, ts.countRate(), 0.005);
+
+  // However, 1 more second after that and we will have filled up all the
+  // buckets, and have to drop one.
+  ts.update(seconds(600));
+  EXPECT_EQ(591, ts.elapsed().count());
+  EXPECT_NEAR(299.5, ts.rate(), 0.01);
+  EXPECT_EQ(299, ts.rate<int>());
+  EXPECT_NEAR(1.5, ts.countRate(), 0.005);
+}
+
+TEST(BucketedTimeSeries, avgTypeConversion) {
+  // Make sure the computed average values are accurate regardless
+  // of the input type and return type.
+
+  {
+    // Simple sanity tests for small positive integer values
+    BucketedTimeSeries<int64_t> ts(60, seconds(600));
+    ts.addValue(seconds(0), 4, 100);
+    ts.addValue(seconds(0), 10, 200);
+    ts.addValue(seconds(0), 16, 100);
+
+    EXPECT_DOUBLE_EQ(10.0, ts.avg());
+    EXPECT_DOUBLE_EQ(10.0, ts.avg<float>());
+    EXPECT_EQ(10, ts.avg<uint64_t>());
+    EXPECT_EQ(10, ts.avg<int64_t>());
+    EXPECT_EQ(10, ts.avg<int32_t>());
+    EXPECT_EQ(10, ts.avg<int16_t>());
+    EXPECT_EQ(10, ts.avg<int8_t>());
+    EXPECT_EQ(10, ts.avg<uint8_t>());
+  }
+
+  {
+    // Test signed integer types with negative values
+    BucketedTimeSeries<int64_t> ts(60, seconds(600));
+    ts.addValue(seconds(0), -100);
+    ts.addValue(seconds(0), -200);
+    ts.addValue(seconds(0), -300);
+    ts.addValue(seconds(0), -200, 65535);
+
+    EXPECT_DOUBLE_EQ(-200.0, ts.avg());
+    EXPECT_DOUBLE_EQ(-200.0, ts.avg<float>());
+    EXPECT_EQ(-200, ts.avg<int64_t>());
+    EXPECT_EQ(-200, ts.avg<int32_t>());
+    EXPECT_EQ(-200, ts.avg<int16_t>());
+  }
+
+  {
+    // Test uint64_t values that would overflow int64_t
+    BucketedTimeSeries<uint64_t> ts(60, seconds(600));
+    ts.addValueAggregated(seconds(0),
+                          std::numeric_limits<uint64_t>::max(),
+                          std::numeric_limits<uint64_t>::max());
+
+    EXPECT_DOUBLE_EQ(1.0, ts.avg());
+    EXPECT_DOUBLE_EQ(1.0, ts.avg<float>());
+    EXPECT_EQ(1, ts.avg<uint64_t>());
+    EXPECT_EQ(1, ts.avg<int64_t>());
+    EXPECT_EQ(1, ts.avg<int8_t>());
+  }
+
+  {
+    // Test doubles with small-ish values that will fit in integer types
+    BucketedTimeSeries<double> ts(60, seconds(600));
+    ts.addValue(seconds(0), 4.0, 100);
+    ts.addValue(seconds(0), 10.0, 200);
+    ts.addValue(seconds(0), 16.0, 100);
+
+    EXPECT_DOUBLE_EQ(10.0, ts.avg());
+    EXPECT_DOUBLE_EQ(10.0, ts.avg<float>());
+    EXPECT_EQ(10, ts.avg<uint64_t>());
+    EXPECT_EQ(10, ts.avg<int64_t>());
+    EXPECT_EQ(10, ts.avg<int32_t>());
+    EXPECT_EQ(10, ts.avg<int16_t>());
+    EXPECT_EQ(10, ts.avg<int8_t>());
+    EXPECT_EQ(10, ts.avg<uint8_t>());
+  }
+
+  {
+    // Test doubles with huge values
+    BucketedTimeSeries<double> ts(60, seconds(600));
+    ts.addValue(seconds(0), 1e19, 100);
+    ts.addValue(seconds(0), 2e19, 200);
+    ts.addValue(seconds(0), 3e19, 100);
+
+    EXPECT_DOUBLE_EQ(ts.avg(), 2e19);
+    EXPECT_NEAR(ts.avg<float>(), 2e19, 1e11);
+  }
+
+  {
+    // Test doubles where the sum adds up larger than a uint64_t,
+    // but the average fits in an int64_t
+    BucketedTimeSeries<double> ts(60, seconds(600));
+    uint64_t value = 0x3fffffffffffffff;
+    FOR_EACH_RANGE(i, 0, 16) {
+      ts.addValue(seconds(0), value);
+    }
+
+    EXPECT_DOUBLE_EQ(value, ts.avg());
+    EXPECT_DOUBLE_EQ(value, ts.avg<float>());
+    // Some precision is lost here due to the huge sum, so the
+    // integer average returned is off by one.
+    EXPECT_NEAR(value, ts.avg<uint64_t>(), 1);
+    EXPECT_NEAR(value, ts.avg<int64_t>(), 1);
+  }
+
+  {
+    // Test BucketedTimeSeries with a smaller integer type
+    BucketedTimeSeries<int16_t> ts(60, seconds(600));
+    FOR_EACH_RANGE(i, 0, 101) {
+      ts.addValue(seconds(0), i);
+    }
+
+    EXPECT_DOUBLE_EQ(50.0, ts.avg());
+    EXPECT_DOUBLE_EQ(50.0, ts.avg<float>());
+    EXPECT_EQ(50, ts.avg<uint64_t>());
+    EXPECT_EQ(50, ts.avg<int64_t>());
+    EXPECT_EQ(50, ts.avg<int16_t>());
+    EXPECT_EQ(50, ts.avg<int8_t>());
+  }
+
+  {
+    // Test BucketedTimeSeries with long double input
+    BucketedTimeSeries<long double> ts(60, seconds(600));
+    ts.addValueAggregated(seconds(0), 1000.0L, 7);
+
+    long double expected = 1000.0L / 7.0L;
+    EXPECT_DOUBLE_EQ(static_cast<double>(expected), ts.avg());
+    EXPECT_DOUBLE_EQ(static_cast<float>(expected), ts.avg<float>());
+    EXPECT_DOUBLE_EQ(expected, ts.avg<long double>());
+    EXPECT_EQ(static_cast<uint64_t>(expected), ts.avg<uint64_t>());
+    EXPECT_EQ(static_cast<int64_t>(expected), ts.avg<int64_t>());
+  }
+
+  {
+    // Test BucketedTimeSeries with int64_t values,
+    // but using an average that requires a fair amount of precision.
+    BucketedTimeSeries<int64_t> ts(60, seconds(600));
+    ts.addValueAggregated(seconds(0), 1000, 7);
+
+    long double expected = 1000.0L / 7.0L;
+    EXPECT_DOUBLE_EQ(static_cast<double>(expected), ts.avg());
+    EXPECT_DOUBLE_EQ(static_cast<float>(expected), ts.avg<float>());
+    EXPECT_DOUBLE_EQ(expected, ts.avg<long double>());
+    EXPECT_EQ(static_cast<uint64_t>(expected), ts.avg<uint64_t>());
+    EXPECT_EQ(static_cast<int64_t>(expected), ts.avg<int64_t>());
+  }
+}
+
+TEST(BucketedTimeSeries, forEachBucket) {
+  typedef BucketedTimeSeries<int64_t>::Bucket Bucket;
+  struct BucketInfo {
+    BucketInfo(const Bucket* b, seconds s, seconds ns)
+      : bucket(b), start(s), nextStart(ns) {}
+
+    const Bucket* bucket;
+    seconds start;
+    seconds nextStart;
+  };
+
+  for (const auto& data : testData) {
+    BucketedTimeSeries<int64_t> ts(data.numBuckets, seconds(data.duration));
+
+    vector<BucketInfo> info;
+    auto fn = [&](const Bucket& bucket, seconds bucketStart,
+                  seconds bucketEnd) -> bool {
+      info.emplace_back(&bucket, bucketStart, bucketEnd);
+      return true;
+    };
+
+    // If we haven't yet added any data, the current bucket will start at 0,
+    // and all data previous buckets will have negative times.
+    ts.forEachBucket(fn);
+
+    CHECK_EQ(data.numBuckets, info.size());
+
+    // Check the data passed in to the function
+    size_t infoIdx = 0;
+    size_t bucketIdx = 1;
+    ssize_t offset = -data.duration;
+    for (size_t n = 0; n < data.numBuckets; ++n) {
+      if (bucketIdx >= data.numBuckets) {
+        bucketIdx = 0;
+        offset += data.duration;
+      }
+
+      EXPECT_EQ(data.bucketStarts[bucketIdx] + offset,
+                info[infoIdx].start.count()) <<
+        data.duration << "x" << data.numBuckets << ": bucketIdx=" <<
+        bucketIdx << ", infoIdx=" << infoIdx;
+
+      size_t nextBucketIdx = bucketIdx + 1;
+      ssize_t nextOffset = offset;
+      if (nextBucketIdx >= data.numBuckets) {
+        nextBucketIdx = 0;
+        nextOffset += data.duration;
+      }
+      EXPECT_EQ(data.bucketStarts[nextBucketIdx] + nextOffset,
+                info[infoIdx].nextStart.count()) <<
+        data.duration << "x" << data.numBuckets << ": bucketIdx=" <<
+        bucketIdx << ", infoIdx=" << infoIdx;
+
+      EXPECT_EQ(&ts.getBucketByIndex(bucketIdx), info[infoIdx].bucket);
+
+      ++bucketIdx;
+      ++infoIdx;
+    }
+  }
+}
+
+TEST(BucketedTimeSeries, queryByIntervalSimple) {
+  BucketedTimeSeries<int> a(3, seconds(12));
+  for (int i = 0; i < 8; i++) {
+    a.addValue(seconds(i), 1);
+  }
+  // We added 1 at each second from 0..7
+  // Query from the time period 0..2.
+  // This is entirely in the first bucket, which has a sum of 4.
+  // The code knows only part of the bucket is covered, and correctly
+  // estimates the desired sum as 3.
+  EXPECT_EQ(2, a.sum(seconds(0), seconds(2)));
+}
+
+TEST(BucketedTimeSeries, queryByInterval) {
+  // Set up a BucketedTimeSeries tracking 6 seconds in 3 buckets
+  const int kNumBuckets = 3;
+  const int kDuration = 6;
+  BucketedTimeSeries<double> b(kNumBuckets, seconds(kDuration));
+
+  for (unsigned int i = 0; i < kDuration; ++i) {
+    // add value 'i' at time 'i'
+    b.addValue(seconds(i), i);
+  }
+
+  // Current bucket state:
+  // 0: time=[0, 2): values=(0, 1), sum=1, count=2
+  // 1: time=[2, 4): values=(2, 3), sum=5, count=1
+  // 2: time=[4, 6): values=(4, 5), sum=9, count=2
+  double expectedSums1[kDuration + 1][kDuration + 1] = {
+    {0,  4.5,   9, 11.5,  14, 14.5,  15},
+    {0,  4.5,   7,  9.5,  10, 10.5,  -1},
+    {0,  2.5,   5,  5.5,   6,   -1,  -1},
+    {0,  2.5,   3,  3.5,  -1,   -1,  -1},
+    {0,  0.5,   1,   -1,  -1,   -1,  -1},
+    {0,  0.5,  -1,   -1,  -1,   -1,  -1},
+    {0,   -1,  -1,   -1,  -1,   -1,  -1}
+  };
+  int expectedCounts1[kDuration + 1][kDuration + 1] = {
+    {0,  1,  2,  3,  4,  5,  6},
+    {0,  1,  2,  3,  4,  5, -1},
+    {0,  1,  2,  3,  4, -1, -1},
+    {0,  1,  2,  3, -1, -1, -1},
+    {0,  1,  2, -1, -1, -1, -1},
+    {0,  1, -1, -1, -1, -1, -1},
+    {0, -1, -1, -1, -1, -1, -1}
+  };
+
+  seconds currentTime = b.getLatestTime() + seconds(1);
+  for (int i = 0; i <= kDuration + 1; i++) {
+    for (int j = 0; j <= kDuration - i; j++) {
+      seconds start = currentTime - seconds(i + j);
+      seconds end = currentTime - seconds(i);
+      double expectedSum = expectedSums1[i][j];
+      EXPECT_EQ(expectedSum, b.sum(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      uint64_t expectedCount = expectedCounts1[i][j];
+      EXPECT_EQ(expectedCount, b.count(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      double expectedAvg = expectedCount ? expectedSum / expectedCount : 0;
+      EXPECT_EQ(expectedAvg, b.avg(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      double expectedRate = j ? expectedSum / j : 0;
+      EXPECT_EQ(expectedRate, b.rate(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+    }
+  }
+
+  // Add 3 more values.
+  // This will overwrite 1 full bucket, and put us halfway through the next.
+  for (unsigned int i = kDuration; i < kDuration + 3; ++i) {
+    b.addValue(seconds(i), i);
+  }
+  EXPECT_EQ(seconds(4), b.getEarliestTime());
+
+  // Current bucket state:
+  // 0: time=[6,  8): values=(6, 7), sum=13, count=2
+  // 1: time=[8, 10): values=(8),    sum=8, count=1
+  // 2: time=[4,  6): values=(4, 5), sum=9, count=2
+  double expectedSums2[kDuration + 1][kDuration + 1] = {
+    {0,    8, 14.5,   21, 25.5,  30,  30},
+    {0,  6.5,   13, 17.5,   22,  22,  -1},
+    {0,  6.5,   11, 15.5, 15.5,  -1,  -1},
+    {0,  4.5,    9,    9,   -1,  -1,  -1},
+    {0,  4.5,  4.5,   -1,   -1,  -1,  -1},
+    {0,    0,   -1,   -1,   -1,  -1,  -1},
+    {0,   -1,   -1,   -1,   -1,  -1,  -1}
+  };
+  int expectedCounts2[kDuration + 1][kDuration + 1] = {
+    {0,  1,  2,  3,  4,  5,  5},
+    {0,  1,  2,  3,  4,  4, -1},
+    {0,  1,  2,  3,  3, -1, -1},
+    {0,  1,  2,  2, -1, -1, -1},
+    {0,  1,  1, -1, -1, -1, -1},
+    {0,  0, -1, -1, -1, -1, -1},
+    {0, -1, -1, -1, -1, -1, -1}
+  };
+
+  currentTime = b.getLatestTime() + seconds(1);
+  for (int i = 0; i <= kDuration + 1; i++) {
+    for (int j = 0; j <= kDuration - i; j++) {
+      seconds start = currentTime - seconds(i + j);
+      seconds end = currentTime - seconds(i);
+      double expectedSum = expectedSums2[i][j];
+      EXPECT_EQ(expectedSum, b.sum(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      uint64_t expectedCount = expectedCounts2[i][j];
+      EXPECT_EQ(expectedCount, b.count(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      double expectedAvg = expectedCount ? expectedSum / expectedCount : 0;
+      EXPECT_EQ(expectedAvg, b.avg(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      seconds dataStart = std::max(start, b.getEarliestTime());
+      seconds dataEnd = std::max(end, dataStart);
+      seconds expectedInterval = dataEnd - dataStart;
+      EXPECT_EQ(expectedInterval, b.elapsed(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+
+      double expectedRate = expectedInterval.count() ?
+        expectedSum / expectedInterval.count() : 0;
+      EXPECT_EQ(expectedRate, b.rate(start, end)) <<
+        "i=" << i << ", j=" << j <<
+        ", interval=[" << start.count() << ", " << end.count() << ")";
+    }
+  }
+}
+
+TEST(BucketedTimeSeries, rateByInterval) {
+  const int kNumBuckets = 5;
+  const seconds kDuration(10);
+  BucketedTimeSeries<double> b(kNumBuckets, kDuration);
+
+  // Add data points at a constant rate of 10 per second.
+  // Start adding data points at kDuration, and fill half of the buckets for
+  // now.
+  seconds start = kDuration;
+  seconds end = kDuration + (kDuration / 2);
+  const double kFixedRate = 10.0;
+  for (seconds i = start; i < end; ++i) {
+    b.addValue(i, kFixedRate);
+  }
+
+  // Querying the rate should yield kFixedRate.
+  EXPECT_EQ(kFixedRate, b.rate());
+  EXPECT_EQ(kFixedRate, b.rate(start, end));
+  EXPECT_EQ(kFixedRate, b.rate(start, start + kDuration));
+  EXPECT_EQ(kFixedRate, b.rate(end - kDuration, end));
+  EXPECT_EQ(kFixedRate, b.rate(end - seconds(1), end));
+  // We have been adding 1 data point per second, so countRate()
+  // should be 1.
+  EXPECT_EQ(1.0, b.countRate());
+  EXPECT_EQ(1.0, b.countRate(start, end));
+  EXPECT_EQ(1.0, b.countRate(start, start + kDuration));
+  EXPECT_EQ(1.0, b.countRate(end - kDuration, end));
+  EXPECT_EQ(1.0, b.countRate(end - seconds(1), end));
+
+  // We haven't added anything before time kDuration.
+  // Querying data earlier than this should result in a rate of 0.
+  EXPECT_EQ(0.0, b.rate(seconds(0), seconds(1)));
+  EXPECT_EQ(0.0, b.countRate(seconds(0), seconds(1)));
+
+  // Fill the remainder of the timeseries from kDuration to kDuration*2
+  start = end;
+  end = kDuration * 2;
+  for (seconds i = start; i < end; ++i) {
+    b.addValue(i, kFixedRate);
+  }
+
+  EXPECT_EQ(kFixedRate, b.rate());
+  EXPECT_EQ(kFixedRate, b.rate(kDuration, kDuration * 2));
+  EXPECT_EQ(kFixedRate, b.rate(seconds(0), kDuration * 2));
+  EXPECT_EQ(kFixedRate, b.rate(seconds(0), kDuration * 10));
+  EXPECT_EQ(1.0, b.countRate());
+  EXPECT_EQ(1.0, b.countRate(kDuration, kDuration * 2));
+  EXPECT_EQ(1.0, b.countRate(seconds(0), kDuration * 2));
+  EXPECT_EQ(1.0, b.countRate(seconds(0), kDuration * 10));
+}
+
+TEST(BucketedTimeSeries, addHistorical) {
+  const int kNumBuckets = 5;
+  const seconds kDuration(10);
+  BucketedTimeSeries<double> b(kNumBuckets, kDuration);
+
+  // Initially fill with a constant rate of data
+  for (seconds i = seconds(0); i < seconds(10); ++i) {
+    b.addValue(i, 10.0);
+  }
+
+  EXPECT_EQ(10.0, b.rate());
+  EXPECT_EQ(10.0, b.avg());
+  EXPECT_EQ(10, b.count());
+
+  // Add some more data points to the middle bucket
+  b.addValue(seconds(4), 40.0);
+  b.addValue(seconds(5), 40.0);
+  EXPECT_EQ(15.0, b.avg());
+  EXPECT_EQ(18.0, b.rate());
+  EXPECT_EQ(12, b.count());
+
+  // Now start adding more current data points, until we are about to roll over
+  // the bucket where we added the extra historical data.
+  for (seconds i = seconds(10); i < seconds(14); ++i) {
+    b.addValue(i, 10.0);
+  }
+  EXPECT_EQ(15.0, b.avg());
+  EXPECT_EQ(18.0, b.rate());
+  EXPECT_EQ(12, b.count());
+
+  // Now roll over the middle bucket
+  b.addValue(seconds(14), 10.0);
+  b.addValue(seconds(15), 10.0);
+  EXPECT_EQ(10.0, b.avg());
+  EXPECT_EQ(10.0, b.rate());
+  EXPECT_EQ(10, b.count());
+
+  // Add more historical values past the bucket window.
+  // These should be ignored.
+  EXPECT_FALSE(b.addValue(seconds(4), 40.0));
+  EXPECT_FALSE(b.addValue(seconds(5), 40.0));
+  EXPECT_EQ(10.0, b.avg());
+  EXPECT_EQ(10.0, b.rate());
+  EXPECT_EQ(10, b.count());
+}
+
+namespace IntMHTS {
+  enum Levels {
+    MINUTE,
+    HOUR,
+    ALLTIME,
+    NUM_LEVELS,
+  };
+
+  const seconds kMinuteHourDurations[] = {
+    seconds(60), seconds(3600), seconds(0)
+  };
+};
+
+TEST(MinuteHourTimeSeries, Basic) {
+  folly::MultiLevelTimeSeries<int> mhts(60, IntMHTS::NUM_LEVELS,
+                                        IntMHTS::kMinuteHourDurations);
+  EXPECT_EQ(mhts.numLevels(), IntMHTS::NUM_LEVELS);
+  EXPECT_EQ(mhts.numLevels(), 3);
+
+  EXPECT_EQ(mhts.sum(IntMHTS::MINUTE), 0);
+  EXPECT_EQ(mhts.sum(IntMHTS::HOUR), 0);
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME), 0);
+
+  EXPECT_EQ(mhts.avg(IntMHTS::MINUTE), 0);
+  EXPECT_EQ(mhts.avg(IntMHTS::HOUR), 0);
+  EXPECT_EQ(mhts.avg(IntMHTS::ALLTIME), 0);
+
+  EXPECT_EQ(mhts.rate(IntMHTS::MINUTE), 0);
+  EXPECT_EQ(mhts.rate(IntMHTS::HOUR), 0);
+  EXPECT_EQ(mhts.rate(IntMHTS::ALLTIME), 0);
+
+  EXPECT_EQ(mhts.getLevel(IntMHTS::MINUTE).elapsed().count(), 0);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::HOUR).elapsed().count(), 0);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::ALLTIME).elapsed().count(), 0);
+
+  seconds cur_time(0);
+
+  mhts.addValue(cur_time++, 10);
+  mhts.flush();
+
+  EXPECT_EQ(mhts.getLevel(IntMHTS::MINUTE).elapsed().count(), 1);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::HOUR).elapsed().count(), 1);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::ALLTIME).elapsed().count(), 1);
+
+  for (int i = 0; i < 299; ++i) {
+    mhts.addValue(cur_time++, 10);
+  }
+  mhts.flush();
+
+  EXPECT_EQ(mhts.getLevel(IntMHTS::MINUTE).elapsed().count(), 60);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::HOUR).elapsed().count(), 300);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::ALLTIME).elapsed().count(), 300);
+
+  EXPECT_EQ(mhts.sum(IntMHTS::MINUTE), 600);
+  EXPECT_EQ(mhts.sum(IntMHTS::HOUR), 300*10);
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME), 300*10);
+
+  EXPECT_EQ(mhts.avg(IntMHTS::MINUTE), 10);
+  EXPECT_EQ(mhts.avg(IntMHTS::HOUR), 10);
+  EXPECT_EQ(mhts.avg(IntMHTS::ALLTIME), 10);
+
+  EXPECT_EQ(mhts.rate(IntMHTS::MINUTE), 10);
+  EXPECT_EQ(mhts.rate(IntMHTS::HOUR), 10);
+  EXPECT_EQ(mhts.rate(IntMHTS::ALLTIME), 10);
+
+  for (int i = 0; i < 3600*3 - 300; ++i) {
+    mhts.addValue(cur_time++, 10);
+  }
+  mhts.flush();
+
+  EXPECT_EQ(mhts.getLevel(IntMHTS::MINUTE).elapsed().count(), 60);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::HOUR).elapsed().count(), 3600);
+  EXPECT_EQ(mhts.getLevel(IntMHTS::ALLTIME).elapsed().count(), 3600*3);
+
+  EXPECT_EQ(mhts.sum(IntMHTS::MINUTE), 600);
+  EXPECT_EQ(mhts.sum(IntMHTS::HOUR), 3600*10);
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME), 3600*3*10);
+
+  EXPECT_EQ(mhts.avg(IntMHTS::MINUTE), 10);
+  EXPECT_EQ(mhts.avg(IntMHTS::HOUR), 10);
+  EXPECT_EQ(mhts.avg(IntMHTS::ALLTIME), 10);
+
+  EXPECT_EQ(mhts.rate(IntMHTS::MINUTE), 10);
+  EXPECT_EQ(mhts.rate(IntMHTS::HOUR), 10);
+  EXPECT_EQ(mhts.rate(IntMHTS::ALLTIME), 10);
+
+  for (int i = 0; i < 3600; ++i) {
+    mhts.addValue(cur_time++, 100);
+  }
+  mhts.flush();
+
+  EXPECT_EQ(mhts.sum(IntMHTS::MINUTE), 60*100);
+  EXPECT_EQ(mhts.sum(IntMHTS::HOUR), 3600*100);
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME),
+            3600*3*10 + 3600*100);
+
+  EXPECT_EQ(mhts.avg(IntMHTS::MINUTE), 100);
+  EXPECT_EQ(mhts.avg(IntMHTS::HOUR), 100);
+  EXPECT_EQ(mhts.avg(IntMHTS::ALLTIME), 32.5);
+
+  EXPECT_EQ(mhts.rate(IntMHTS::MINUTE), 100);
+  EXPECT_EQ(mhts.rate(IntMHTS::HOUR), 100);
+  EXPECT_EQ(mhts.rate(IntMHTS::ALLTIME), 32);
+
+  for (int i = 0; i < 1800; ++i) {
+    mhts.addValue(cur_time++, 120);
+  }
+  mhts.flush();
+
+  EXPECT_EQ(mhts.sum(IntMHTS::MINUTE), 60*120);
+  EXPECT_EQ(mhts.sum(IntMHTS::HOUR),
+            1800*100 + 1800*120);
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME),
+            3600*3*10 + 3600*100 + 1800*120);
+
+  for (int i = 0; i < 60; ++i) {
+    mhts.addValue(cur_time++, 1000);
+  }
+  mhts.flush();
+
+  EXPECT_EQ(mhts.sum(IntMHTS::MINUTE), 60*1000);
+  EXPECT_EQ(mhts.sum(IntMHTS::HOUR),
+            1740*100 + 1800*120 + 60*1000);
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME),
+            3600*3*10 + 3600*100 + 1800*120 + 60*1000);
+
+  mhts.clear();
+  EXPECT_EQ(mhts.sum(IntMHTS::ALLTIME), 0);
+}
+
+TEST(MinuteHourTimeSeries, QueryByInterval) {
+  folly::MultiLevelTimeSeries<int> mhts(60, IntMHTS::NUM_LEVELS,
+                                        IntMHTS::kMinuteHourDurations);
+
+  seconds curTime(0);
+  for (curTime = seconds(0); curTime < seconds(7200); curTime++) {
+    mhts.addValue(curTime, 1);
+  }
+  for (curTime = seconds(7200); curTime < seconds(7200 + 3540); curTime++) {
+    mhts.addValue(curTime, 10);
+  }
+  for (curTime = seconds(7200 + 3540); curTime < seconds(7200 + 3600);
+       curTime++) {
+    mhts.addValue(curTime, 100);
+  }
+  mhts.flush();
+
+  struct TimeInterval {
+    seconds start;
+    seconds end;
+  };
+  TimeInterval intervals[12] = {
+    { curTime - seconds(60), curTime },
+    { curTime - seconds(3600), curTime },
+    { curTime - seconds(7200), curTime },
+    { curTime - seconds(3600), curTime - seconds(60) },
+    { curTime - seconds(7200), curTime - seconds(60) },
+    { curTime - seconds(7200), curTime - seconds(3600) },
+    { curTime - seconds(50), curTime - seconds(20) },
+    { curTime - seconds(3020), curTime - seconds(20) },
+    { curTime - seconds(7200), curTime - seconds(20) },
+    { curTime - seconds(3000), curTime - seconds(1000) },
+    { curTime - seconds(7200), curTime - seconds(1000) },
+    { curTime - seconds(7200), curTime - seconds(3600) },
+  };
+
+  int expectedSums[12] = {
+    6000, 41400, 32400, 35400, 32130, 16200, 3000, 33600, 32310, 20000, 27900,
+    16200
+  };
+
+  int expectedCounts[12] = {
+    60, 3600, 7200, 3540, 7140, 3600, 30, 3000, 7180, 2000, 6200, 3600
+  };
+
+  for (int i = 0; i < 12; ++i) {
+    TimeInterval interval = intervals[i];
+
+    int s = mhts.sum(interval.start, interval.end);
+    EXPECT_EQ(expectedSums[i], s);
+
+    int c = mhts.count(interval.start, interval.end);
+    EXPECT_EQ(expectedCounts[i], c);
+
+    int a = mhts.avg<int>(interval.start, interval.end);
+    EXPECT_EQ(expectedCounts[i] ?
+              (expectedSums[i] / expectedCounts[i]) : 0,
+              a);
+
+    int r = mhts.rate<int>(interval.start, interval.end);
+    int expectedRate =
+      expectedSums[i] / (interval.end - interval.start).count();
+    EXPECT_EQ(expectedRate, r);
+  }
+}
diff --git a/faux-folly/folly/test/TokenBucketTest.cpp b/faux-folly/folly/test/TokenBucketTest.cpp
new file mode 100644
index 0000000..1709913
--- /dev/null
+++ b/faux-folly/folly/test/TokenBucketTest.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 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/test/TokenBucketTest.h>
+
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+TEST(TokenBucket, ReverseTime) {
+  const double rate = 1000;
+  TokenBucket tokenBucket(rate, rate * 0.01 + 1e-6, 0);
+  size_t count = 0;
+  while (tokenBucket.consume(1, 0.1)) {
+    count += 1;
+  }
+  EXPECT_EQ(10, count);
+  // Going backwards in time has no affect on the toke count (this protects
+  // against different threads providing out of order timestamps).
+  double tokensBefore = tokenBucket.available();
+  EXPECT_FALSE(tokenBucket.consume(1, 0.09999999));
+  EXPECT_EQ(tokensBefore, tokenBucket.available());
+}
+
+TEST_P(TokenBucketTest, sanity) {
+  std::pair<double, double> params = GetParam();
+  double rate = params.first;
+  double consumeSize = params.second;
+
+  const double tenMillisecondBurst = rate * 0.010;
+  // Select a burst size of 10 milliseconds at the max rate or the consume size
+  // if 10 ms at rate is too small.
+  const double burstSize = std::max(consumeSize, tenMillisecondBurst);
+  TokenBucket tokenBucket(rate, burstSize, 0);
+  double tokenCounter = 0;
+  double currentTime = 0;
+  // Simulate time advancing 10 seconds
+  for (; currentTime <= 10.0; currentTime += 0.001) {
+    EXPECT_FALSE(tokenBucket.consume(burstSize + 1, currentTime));
+    while (tokenBucket.consume(consumeSize, currentTime)) {
+      tokenCounter += consumeSize;
+    }
+    // Tokens consumed should exceed some lower bound based on rate.
+    // Note: The token bucket implementation is not precise, so the lower bound
+    // is somewhat fudged. The upper bound is accurate however.
+    EXPECT_LE(rate * currentTime * 0.9 - 1, tokenCounter);
+    // Tokens consumed should not exceed some upper bound based on rate.
+    EXPECT_GE(rate * currentTime + 1e-6, tokenCounter);
+  }
+}
+
+static std::vector<std::pair<double, double> > rateToConsumeSize = {
+  {100, 1},
+  {1000, 1},
+  {10000, 1},
+  {10000, 5},
+};
+
+INSTANTIATE_TEST_CASE_P(TokenBucket,
+                        TokenBucketTest,
+                        ::testing::ValuesIn(rateToConsumeSize));
diff --git a/faux-folly/folly/test/TokenBucketTest.h b/faux-folly/folly/test/TokenBucketTest.h
new file mode 100644
index 0000000..0e7d2a5
--- /dev/null
+++ b/faux-folly/folly/test/TokenBucketTest.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 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 <gtest/gtest.h>
+
+#include <folly/TokenBucket.h>
+
+namespace folly {
+
+struct TokenBucketTest :
+    public ::testing::TestWithParam<std::pair<double,double> > {};
+
+} // folly
diff --git a/faux-folly/folly/test/TraitsTest.cpp b/faux-folly/folly/test/TraitsTest.cpp
new file mode 100644
index 0000000..e3776e2
--- /dev/null
+++ b/faux-folly/folly/test/TraitsTest.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 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/Benchmark.h>
+#include <folly/Traits.h>
+
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+using namespace std;
+
+struct T1 {}; // old-style IsRelocatable, below
+struct T2 {}; // old-style IsRelocatable, below
+struct T3 { typedef std::true_type IsRelocatable; };
+struct T4 { typedef std::true_type IsTriviallyCopyable; };
+struct T5 : T3 {};
+
+struct F1 {};
+struct F2 { typedef int IsRelocatable; };
+struct F3 : T3 { typedef std::false_type IsRelocatable; };
+struct F4 : T1 {};
+
+namespace folly {
+  template <> struct IsRelocatable<T1> : std::true_type {};
+  template <> FOLLY_ASSUME_RELOCATABLE(T2);
+}
+
+TEST(Traits, scalars) {
+  EXPECT_TRUE(IsRelocatable<int>::value);
+  EXPECT_TRUE(IsRelocatable<bool>::value);
+  EXPECT_TRUE(IsRelocatable<double>::value);
+  EXPECT_TRUE(IsRelocatable<void*>::value);
+}
+
+TEST(Traits, containers) {
+  EXPECT_TRUE  (IsRelocatable<vector<F1>>::value);
+  EXPECT_FALSE((IsRelocatable<pair<F1, F1>>::value));
+  EXPECT_TRUE ((IsRelocatable<pair<T1, T2>>::value));
+}
+
+TEST(Traits, original) {
+  EXPECT_TRUE(IsRelocatable<T1>::value);
+  EXPECT_TRUE(IsRelocatable<T2>::value);
+}
+
+TEST(Traits, typedefd) {
+  EXPECT_TRUE (IsRelocatable<T3>::value);
+  EXPECT_TRUE (IsRelocatable<T5>::value);
+  EXPECT_FALSE(IsRelocatable<F2>::value);
+  EXPECT_FALSE(IsRelocatable<F3>::value);
+}
+
+TEST(Traits, unset) {
+  EXPECT_FALSE(IsRelocatable<F1>::value);
+  EXPECT_FALSE(IsRelocatable<F4>::value);
+}
+
+TEST(Traits, bitprop) {
+  EXPECT_TRUE(IsTriviallyCopyable<T4>::value);
+  EXPECT_TRUE(IsRelocatable<T4>::value);
+}
+
+TEST(Traits, bitAndInit) {
+  EXPECT_TRUE (IsTriviallyCopyable<int>::value);
+  EXPECT_FALSE(IsTriviallyCopyable<vector<int>>::value);
+  EXPECT_TRUE (IsZeroInitializable<int>::value);
+  EXPECT_FALSE(IsZeroInitializable<vector<int>>::value);
+}
+
+TEST(Traits, is_negative) {
+  EXPECT_TRUE(folly::is_negative(-1));
+  EXPECT_FALSE(folly::is_negative(0));
+  EXPECT_FALSE(folly::is_negative(1));
+  EXPECT_FALSE(folly::is_negative(0u));
+  EXPECT_FALSE(folly::is_negative(1u));
+
+  EXPECT_TRUE(folly::is_non_positive(-1));
+  EXPECT_TRUE(folly::is_non_positive(0));
+  EXPECT_FALSE(folly::is_non_positive(1));
+  EXPECT_TRUE(folly::is_non_positive(0u));
+  EXPECT_FALSE(folly::is_non_positive(1u));
+}
+
+TEST(Traits, relational) {
+  // We test, especially, the edge cases to make sure we don't
+  // trip -Wtautological-comparisons
+
+  EXPECT_FALSE((folly::less_than<uint8_t, 0u,   uint8_t>(0u)));
+  EXPECT_FALSE((folly::less_than<uint8_t, 0u,   uint8_t>(254u)));
+  EXPECT_FALSE((folly::less_than<uint8_t, 255u, uint8_t>(255u)));
+  EXPECT_TRUE( (folly::less_than<uint8_t, 255u, uint8_t>(254u)));
+
+  EXPECT_FALSE((folly::greater_than<uint8_t, 0u,   uint8_t>(0u)));
+  EXPECT_TRUE( (folly::greater_than<uint8_t, 0u,   uint8_t>(254u)));
+  EXPECT_FALSE((folly::greater_than<uint8_t, 255u, uint8_t>(255u)));
+  EXPECT_FALSE((folly::greater_than<uint8_t, 255u, uint8_t>(254u)));
+}
+
+struct membership_no {};
+struct membership_yes { using x = void; };
+FOLLY_CREATE_HAS_MEMBER_TYPE_TRAITS(has_member_type_x, x);
+
+TEST(Traits, has_member_type) {
+  EXPECT_FALSE(bool(has_member_type_x<membership_no>::value));
+  EXPECT_TRUE(bool(has_member_type_x<membership_yes>::value));
+}
+
+int main(int argc, char ** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  if (FLAGS_benchmark) {
+    folly::runBenchmarks();
+  }
+  return RUN_ALL_TESTS();
+}
diff --git a/faux-folly/folly/test/VarintTest.cpp b/faux-folly/folly/test/VarintTest.cpp
new file mode 100644
index 0000000..22076af
--- /dev/null
+++ b/faux-folly/folly/test/VarintTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2015 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/Varint.h>
+
+#include <array>
+#include <initializer_list>
+#include <random>
+#include <vector>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+#include <folly/Random.h>
+
+DEFINE_int32(random_seed, folly::randomNumberSeed(), "random seed");
+
+namespace folly { namespace test {
+
+void testVarint(uint64_t val, std::initializer_list<uint8_t> bytes) {
+  size_t n = bytes.size();
+  ByteRange expected(&*bytes.begin(), n);
+
+  {
+    uint8_t buf[kMaxVarintLength64];
+    EXPECT_EQ(expected.size(), encodeVarint(val, buf));
+    EXPECT_TRUE(ByteRange(buf, expected.size()) == expected);
+  }
+
+  {
+    ByteRange r = expected;
+    uint64_t decoded = decodeVarint(r);
+    EXPECT_TRUE(r.empty());
+    EXPECT_EQ(val, decoded);
+  }
+
+  if (n < kMaxVarintLength64) {
+    // Try from a full buffer too, different code path
+    uint8_t buf[kMaxVarintLength64];
+    memcpy(buf, &*bytes.begin(), n);
+
+    uint8_t fills[] = {0, 0x7f, 0x80, 0xff};
+
+    for (uint8_t fill : fills) {
+      memset(buf + n, fill, kMaxVarintLength64 - n);
+      ByteRange r(buf, kMaxVarintLength64);
+      uint64_t decoded = decodeVarint(r);
+      EXPECT_EQ(val, decoded);
+      EXPECT_EQ(kMaxVarintLength64 - n, r.size());
+    }
+  }
+}
+
+TEST(Varint, Interface) {
+  // Make sure decodeVarint() accepts all of StringPiece, MutableStringPiece,
+  // ByteRange, and MutableByteRange.
+  char c = 0;
+
+  StringPiece sp(&c, 1);
+  EXPECT_EQ(decodeVarint(sp), 0);
+
+  MutableStringPiece msp(&c, 1);
+  EXPECT_EQ(decodeVarint(msp), 0);
+
+  ByteRange br(reinterpret_cast<unsigned char*>(&c), 1);
+  EXPECT_EQ(decodeVarint(br), 0);
+
+  MutableByteRange mbr(reinterpret_cast<unsigned char*>(&c), 1);
+  EXPECT_EQ(decodeVarint(mbr), 0);
+}
+
+TEST(Varint, Simple) {
+  testVarint(0, {0});
+  testVarint(1, {1});
+  testVarint(127, {127});
+  testVarint(128, {0x80, 0x01});
+  testVarint(300, {0xac, 0x02});
+  testVarint(16383, {0xff, 0x7f});
+  testVarint(16384, {0x80, 0x80, 0x01});
+
+  testVarint(static_cast<uint32_t>(-1),
+             {0xff, 0xff, 0xff, 0xff, 0x0f});
+  testVarint(static_cast<uint64_t>(-1),
+             {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01});
+}
+
+TEST(ZigZag, Simple) {
+  EXPECT_EQ(0, encodeZigZag(0));
+  EXPECT_EQ(1, encodeZigZag(-1));
+  EXPECT_EQ(2, encodeZigZag(1));
+  EXPECT_EQ(3, encodeZigZag(-2));
+  EXPECT_EQ(4, encodeZigZag(2));
+
+  EXPECT_EQ(0,  decodeZigZag(0));
+  EXPECT_EQ(-1, decodeZigZag(1));
+  EXPECT_EQ(1,  decodeZigZag(2));
+  EXPECT_EQ(-2, decodeZigZag(3));
+  EXPECT_EQ(2,  decodeZigZag(4));
+}
+
+namespace {
+
+constexpr size_t kNumValues = 1000;
+std::vector<uint64_t> gValues;
+std::vector<uint64_t> gDecodedValues;
+std::vector<uint8_t> gEncoded;
+
+void generateRandomValues() {
+  LOG(INFO) << "Random seed is " << FLAGS_random_seed;
+  std::mt19937 rng(FLAGS_random_seed);
+
+  // Approximation of power law
+  std::uniform_int_distribution<int> numBytes(1, 8);
+  std::uniform_int_distribution<int> byte(0, 255);
+
+  gValues.resize(kNumValues);
+  gDecodedValues.resize(kNumValues);
+  gEncoded.resize(kNumValues * kMaxVarintLength64);
+  for (size_t i = 0; i < kNumValues; ++i) {
+    int n = numBytes(rng);
+    uint64_t val = 0;
+    for (int j = 0; j < n; ++j) {
+      val = (val << 8) + byte(rng);
+    }
+    gValues[i] = val;
+  }
+}
+
+// Benchmark results (Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz, Linux x86_64)
+//
+// I0814 19:13:14.466256  7504 VarintTest.cpp:146] Random seed is -1216518886
+// ============================================================================
+// folly/test/VarintTest.cpp                       relative  time/iter  iters/s
+// ============================================================================
+// VarintEncoding                                               6.69us  149.37K
+// VarintDecoding                                               6.85us  145.90K
+// ============================================================================
+//
+// Disabling the "fast path" code in decodeVarint hurts performance:
+//
+// I0814 19:15:13.871467  9550 VarintTest.cpp:156] Random seed is -1216518886
+// ============================================================================
+// folly/test/VarintTest.cpp                       relative  time/iter  iters/s
+// ============================================================================
+// VarintEncoding                                               6.75us  148.26K
+// VarintDecoding                                              12.60us   79.37K
+// ============================================================================
+
+BENCHMARK(VarintEncoding, iters) {
+  uint8_t* start = &(*gEncoded.begin());
+  uint8_t* p = start;
+  bool empty = (iters == 0);
+  while (iters--) {
+    p = start;
+    for (auto& v : gValues) {
+      p += encodeVarint(v, p);
+    }
+  }
+
+  gEncoded.erase(gEncoded.begin() + (p - start), gEncoded.end());
+}
+
+BENCHMARK(VarintDecoding, iters) {
+  while (iters--) {
+    size_t i = 0;
+    ByteRange range(&(*gEncoded.begin()), &(*gEncoded.end()));
+    while (!range.empty()) {
+      gDecodedValues[i++] = decodeVarint(range);
+    }
+  }
+}
+
+}  // namespace
+
+}}  // namespaces
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  google::InitGoogleLogging(argv[0]);
+  int ret = RUN_ALL_TESTS();
+  if (ret == 0) {
+    folly::test::generateRandomValues();
+    folly::runBenchmarksOnFlag();
+  }
+  return ret;
+}
diff --git a/faux-folly/folly/test/function_benchmark/Makefile.am b/faux-folly/folly/test/function_benchmark/Makefile.am
new file mode 100644
index 0000000..c363fc2
--- /dev/null
+++ b/faux-folly/folly/test/function_benchmark/Makefile.am
@@ -0,0 +1,11 @@
+ACLOCAL_AMFLAGS = -I m4
+
+# depends on libfollybenchmark
+
+# TESTS = function_benchmark
+
+# check_PROGRAMS = $(TESTS)
+
+# noinst_HEADERS = test_functions.h benchmark_impl.h
+
+# function_benchmark_SOURCES = benchmark_impl.cpp main.cpp test_functions.cpp
diff --git a/faux-folly/folly/test/function_benchmark/benchmark_impl.cpp b/faux-folly/folly/test/function_benchmark/benchmark_impl.cpp
new file mode 100644
index 0000000..db6ad9c
--- /dev/null
+++ b/faux-folly/folly/test/function_benchmark/benchmark_impl.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 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/test/function_benchmark/benchmark_impl.h>
+
+#include <folly/test/function_benchmark/test_functions.h>
+
+/*
+ * These functions are defined in a separate file so that gcc won't be able to
+ * inline them and optimize away the indirect calls.
+ */
+
+void BM_fn_ptr_invoke_impl(int iters, void (*fn)()) {
+  for (int n = 0; n < iters; ++n) {
+    fn();
+  }
+}
+
+void BM_std_function_invoke_impl(int iters,
+                                 const std::function<void()>& fn) {
+  for (int n = 0; n < iters; ++n) {
+    fn();
+  }
+}
+
+void BM_mem_fn_invoke_impl(int iters,
+                           TestClass* tc,
+                           void (TestClass::*memfn)()) {
+  for (int n = 0; n < iters; ++n) {
+    (tc->*memfn)();
+  }
+}
+
+void BM_virtual_fn_invoke_impl(int iters, VirtualClass* vc) {
+  for (int n = 0; n < iters; ++n) {
+    vc->doNothing();
+  }
+}
diff --git a/faux-folly/folly/test/function_benchmark/benchmark_impl.h b/faux-folly/folly/test/function_benchmark/benchmark_impl.h
new file mode 100644
index 0000000..3d99265
--- /dev/null
+++ b/faux-folly/folly/test/function_benchmark/benchmark_impl.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 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.
+ */
+#ifndef BENCHMARK_IMPL_H_
+#define BENCHMARK_IMPL_H_
+
+#include <functional>
+
+class TestClass;
+class VirtualClass;
+
+void BM_fn_ptr_invoke_impl(int iters, void (*fn)());
+void BM_std_function_invoke_impl(int iters, const std::function<void()>& fn);
+void BM_mem_fn_invoke_impl(int iters,
+                           TestClass* tc,
+                           void (TestClass::*memfn)());
+void BM_virtual_fn_invoke_impl(int iters, VirtualClass* vc);
+
+// Inlined version of BM_fn_ptr_invoke_impl().
+// The compiler could potentially even optimize the call to the function
+// pointer if it is a constexpr.
+inline void BM_fn_ptr_invoke_inlined_impl(int iters, void (*fn)()) {
+  for (int n = 0; n < iters; ++n) {
+    fn();
+  }
+}
+
+// Invoke a function object as a template parameter.
+// This can be used to directly invoke lambda functions
+template<typename T>
+void BM_invoke_fn_template_impl(int iters, const T& fn) {
+  for (int n = 0; n < iters; ++n) {
+    fn();
+  }
+}
+
+#endif // BENCHMARK_IMPL_H_
diff --git a/faux-folly/folly/test/function_benchmark/main.cpp b/faux-folly/folly/test/function_benchmark/main.cpp
new file mode 100644
index 0000000..e269262
--- /dev/null
+++ b/faux-folly/folly/test/function_benchmark/main.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2015 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/test/function_benchmark/benchmark_impl.h>
+#include <folly/test/function_benchmark/test_functions.h>
+
+#include <folly/Benchmark.h>
+#include <folly/ScopeGuard.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+using folly::ScopeGuard;
+using folly::makeGuard;
+
+// Declare the bm_max_iters flag from folly/Benchmark.cpp
+DECLARE_int32(bm_max_iters);
+
+// Directly invoking a function
+BENCHMARK(fn_invoke, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    doNothing();
+  }
+}
+
+// Invoking a function through a function pointer
+BENCHMARK(fn_ptr_invoke, iters) {
+  BM_fn_ptr_invoke_impl(iters, doNothing);
+}
+
+// Invoking a function through a std::function object
+BENCHMARK(std_function_invoke, iters) {
+  BM_std_function_invoke_impl(iters, doNothing);
+}
+
+// Invoking a member function through a member function pointer
+BENCHMARK(mem_fn_invoke, iters) {
+  TestClass tc;
+  BM_mem_fn_invoke_impl(iters, &tc, &TestClass::doNothing);
+}
+
+// Invoke a function pointer through an inlined wrapper function
+BENCHMARK(fn_ptr_invoke_through_inline, iters) {
+  BM_fn_ptr_invoke_inlined_impl(iters, doNothing);
+}
+
+// Invoke a lambda that calls doNothing() through an inlined wrapper function
+BENCHMARK(lambda_invoke_fn, iters) {
+  BM_invoke_fn_template_impl(iters, [] { doNothing(); });
+}
+
+// Invoke a lambda that does nothing
+BENCHMARK(lambda_noop, iters) {
+  BM_invoke_fn_template_impl(iters, [] {});
+}
+
+// Invoke a lambda that modifies a local variable
+BENCHMARK(lambda_local_var, iters) {
+  uint32_t count1 = 0;
+  uint32_t count2 = 0;
+  BM_invoke_fn_template_impl(iters, [&] {
+    // Do something slightly more complicated than just incrementing a
+    // variable.  Otherwise gcc is smart enough to optimize the loop away.
+    if (count1 & 0x1) {
+      ++count2;
+    }
+    ++count1;
+  });
+
+  // Use the values we computed, so gcc won't optimize the loop away
+  CHECK_EQ(iters, count1);
+  CHECK_EQ(iters / 2, count2);
+}
+
+// Invoke a function pointer through the same wrapper used for lambdas
+BENCHMARK(fn_ptr_invoke_through_template, iters) {
+  BM_invoke_fn_template_impl(iters, doNothing);
+}
+
+// Invoking a virtual method
+BENCHMARK(virtual_fn_invoke, iters) {
+  VirtualClass vc;
+  BM_virtual_fn_invoke_impl(iters, &vc);
+}
+
+// Creating a function pointer and invoking it
+BENCHMARK(fn_ptr_create_invoke, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    void (*fn)() = doNothing;
+    fn();
+  }
+}
+
+// Creating a std::function object from a function pointer, and invoking it
+BENCHMARK(std_function_create_invoke, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    std::function<void()> fn = doNothing;
+    fn();
+  }
+}
+
+// Creating a pointer-to-member and invoking it
+BENCHMARK(mem_fn_create_invoke, iters) {
+  TestClass tc;
+  for (size_t n = 0; n < iters; ++n) {
+    void (TestClass::*memfn)() = &TestClass::doNothing;
+    (tc.*memfn)();
+  }
+}
+
+// Using std::bind to create a std::function from a member function,
+// and invoking it
+BENCHMARK(std_bind_create_invoke, iters) {
+  TestClass tc;
+  for (size_t n = 0; n < iters; ++n) {
+    std::function<void()> fn = std::bind(&TestClass::doNothing, &tc);
+    fn();
+  }
+}
+
+// Using std::bind directly to invoke a member function
+BENCHMARK(std_bind_direct_invoke, iters) {
+  TestClass tc;
+  for (size_t n = 0; n < iters; ++n) {
+    auto fn = std::bind(&TestClass::doNothing, &tc);
+    fn();
+  }
+}
+
+// Using ScopeGuard to invoke a std::function
+BENCHMARK(scope_guard_std_function, iters) {
+  std::function<void()> fn(doNothing);
+  for (size_t n = 0; n < iters; ++n) {
+    ScopeGuard g = makeGuard(fn);
+  }
+}
+
+// Using ScopeGuard to invoke a std::function,
+// but create the ScopeGuard with an rvalue to a std::function
+BENCHMARK(scope_guard_std_function_rvalue, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    ScopeGuard g = makeGuard(std::function<void()>(doNothing));
+  }
+}
+
+// Using ScopeGuard to invoke a function pointer
+BENCHMARK(scope_guard_fn_ptr, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    ScopeGuard g = makeGuard(doNothing);
+  }
+}
+
+// Using ScopeGuard to invoke a lambda that does nothing
+BENCHMARK(scope_guard_lambda_noop, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    ScopeGuard g = makeGuard([] {});
+  }
+}
+
+// Using ScopeGuard to invoke a lambda that invokes a function
+BENCHMARK(scope_guard_lambda_function, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    ScopeGuard g = makeGuard([] { doNothing(); });
+  }
+}
+
+// Using ScopeGuard to invoke a lambda that modifies a local variable
+BENCHMARK(scope_guard_lambda_local_var, iters) {
+  uint32_t count = 0;
+  for (size_t n = 0; n < iters; ++n) {
+    ScopeGuard g = makeGuard([&] {
+      // Increment count if n is odd.  Without this conditional check
+      // (i.e., if we just increment count each time through the loop),
+      // gcc is smart enough to optimize the entire loop away, and just set
+      // count = iters.
+      if (n & 0x1) {
+        ++count;
+      }
+    });
+  }
+
+  // Check that the value of count is what we expect.
+  // This check is necessary: if we don't use count, gcc detects that count is
+  // unused and optimizes the entire loop away.
+  CHECK_EQ(iters / 2, count);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(throw_exception, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    try {
+      throwException();
+    } catch (const std::exception& ex) {
+    }
+  }
+}
+
+BENCHMARK(catch_no_exception, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    try {
+      doNothing();
+    } catch (const std::exception& ex) {
+    }
+  }
+}
+
+BENCHMARK(return_exc_ptr, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    returnExceptionPtr();
+  }
+}
+
+BENCHMARK(exc_ptr_param_return, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    std::exception_ptr ex;
+    exceptionPtrReturnParam(&ex);
+  }
+}
+
+BENCHMARK(exc_ptr_param_return_null, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    exceptionPtrReturnParam(nullptr);
+  }
+}
+
+BENCHMARK(return_string, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    returnString();
+  }
+}
+
+BENCHMARK(return_string_noexcept, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    returnStringNoExcept();
+  }
+}
+
+BENCHMARK(return_code, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    returnCode(false);
+  }
+}
+
+BENCHMARK(return_code_noexcept, iters) {
+  for (size_t n = 0; n < iters; ++n) {
+    returnCodeNoExcept(false);
+  }
+}
+
+// main()
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  folly::runBenchmarks();
+}
diff --git a/faux-folly/folly/test/function_benchmark/test_functions.cpp b/faux-folly/folly/test/function_benchmark/test_functions.cpp
new file mode 100644
index 0000000..8eaf51a
--- /dev/null
+++ b/faux-folly/folly/test/function_benchmark/test_functions.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 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/test/function_benchmark/test_functions.h>
+
+/*
+ * These functions are defined in a separate file so that
+ * gcc won't be able to inline them.
+ */
+
+
+class Exception : public std::exception {
+ public:
+  explicit Exception(const std::string& value) : value_(value) {}
+  ~Exception(void) noexcept override {}
+
+  const char* what(void) const noexcept override { return value_.c_str(); }
+
+ private:
+  std::string value_;
+};
+
+void doNothing() {
+}
+
+void throwException() {
+  throw Exception("this is a test");
+}
+
+std::exception_ptr returnExceptionPtr() {
+  Exception ex("this is a test");
+  return std::make_exception_ptr(ex);
+}
+
+void exceptionPtrReturnParam(std::exception_ptr* excReturn) {
+  if (excReturn) {
+    Exception ex("this is a test");
+    *excReturn = std::make_exception_ptr(ex);
+  }
+}
+
+std::string returnString() {
+  return "this is a test";
+}
+
+std::string returnStringNoExcept() noexcept {
+  return "this is a test";
+}
+
+int returnCode(int value) {
+  return value;
+}
+
+int returnCodeNoExcept(int value) noexcept {
+  return value;
+}
+
+void TestClass::doNothing() {
+}
+
+VirtualClass::~VirtualClass() {
+}
+
+void VirtualClass::doNothing() {
+};
diff --git a/faux-folly/folly/test/function_benchmark/test_functions.h b/faux-folly/folly/test/function_benchmark/test_functions.h
new file mode 100644
index 0000000..9906d7a
--- /dev/null
+++ b/faux-folly/folly/test/function_benchmark/test_functions.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef TEST_FUNCTIONS_H_
+#define TEST_FUNCTIONS_H_
+
+#include <exception>
+#include <string>
+
+void doNothing();
+
+void throwException();
+std::exception_ptr returnExceptionPtr();
+void exceptionPtrReturnParam(std::exception_ptr* excReturn);
+std::string returnString();
+std::string returnStringNoExcept() noexcept;
+int returnCode(int value);
+int returnCodeNoExcept(int value) noexcept;
+
+class TestClass {
+ public:
+  void doNothing();
+};
+
+class VirtualClass {
+ public:
+  virtual ~VirtualClass();
+  virtual void doNothing();
+};
+
+#endif // TEST_FUNCTIONS_H_
diff --git a/faux-folly/folly/test/json_test_data/commented.json b/faux-folly/folly/test/json_test_data/commented.json
new file mode 100644
index 0000000..80632cb
--- /dev/null
+++ b/faux-folly/folly/test/json_test_data/commented.json
@@ -0,0 +1,12 @@
+{
+  // comment
+  "test": "foo", // comment
+  "test2": "foo // bar", // more comments
+  /*
+  "test3": "baz"
+  */
+  "test4": "foo /* bar", /* comment */
+  "te//": "foo",
+  "te/*": "bar",
+  "\\\"": "\\" /* comment */
+}
diff --git a/faux-folly/folly/test/json_test_data/commented.json.exp b/faux-folly/folly/test/json_test_data/commented.json.exp
new file mode 100644
index 0000000..0c8482f
--- /dev/null
+++ b/faux-folly/folly/test/json_test_data/commented.json.exp
@@ -0,0 +1,7 @@
+{
+    "test": "foo",   "test2": "foo // bar",   
+  "test4": "foo /* bar", 
+  "te//": "foo",
+  "te/*": "bar",
+  "\\\"": "\\" 
+}
diff --git a/faux-folly/folly/test/stl_tests/StlVectorTest.cpp b/faux-folly/folly/test/stl_tests/StlVectorTest.cpp
new file mode 100644
index 0000000..425843c
--- /dev/null
+++ b/faux-folly/folly/test/stl_tests/StlVectorTest.cpp
@@ -0,0 +1,2742 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// @author Nicholas Ormrod <njormrod@fb.com>
+
+/*
+
+This file contains an extensive STL compliance test suite for an STL vector
+implementation (such as FBVector).
+
+GCC 4.7 is required.
+
+*/
+
+// only compile if GCC is at least 4.7
+#if __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7
+
+#if 0
+#define USING_STD_VECTOR
+#endif
+
+/*
+
+The insanity of this file deserves a superficial explanation.
+
+This file tests an implementation of STL vector. It is extremely comprehensive.
+If it compiles (more on that later) it generates a binary which, when run,
+exhaustively tests its vector for standard compliance.
+
+Limitations:
+-If it doesn't compile, the compiler errors are mind-boggling.
+-Not everything is testable. There are a few comments in the code where
+ the implementation must be inspected, as opposed to tested. These are very
+ simple inspections. Search for 'whitebox'.
+-It does not test boolean specialization.
+
+==========================
+How this file is organized
+
+--------------
+Data and Alloc
+
+Data is a class designed to provide diagnostics when stored in a vector. It
+counts the number of operations performed on it, can have any function
+disabled or labeled as noexcept, throws errors from anywhere that is not
+noexcept, tracks its supposed location in memory (optional), tracks
+aggregate information, and can print a trace of its action.
+
+Alloc, like Data, is a full-blown diagnostic allocator. It keeps track of
+all space it has allocated, keeps counters, throws exceptions, and can easily
+compare equal or not equal with other Allocs.
+
+These two classes have a few useful helper functions:
+isSane - checks that all the tracked variables make sense
+softReset - simplifies the variables before a test
+hardReset - brutally resets all variables to the default state
+
+--------
+STL_TEST
+
+Google test is not quite good enough for this test file, because we need to
+run tests across different input values and different types.
+
+The STL_TEST macro takes a few arguments:
+string - what is being tested
+id - unique id, passed to TEST
+restriction - requirements for test types
+parameters - which variables to range over
+
+Eg: STL_TEST("23.2.3", isCopyable, is_copy_constructible, a) { ... }
+
+The restriction is used to select which types get tested. Copy construction,
+for example, requires a data type which is copy constructible, whereas to test
+the clear operation, the data only needs to be destructible. If the type does
+not pass the restriction, then the test is not instantiated with that type (if
+it were, then there would be a compiler error).
+
+The variable names in the standard have very specific meaning. For example,
+a and b are always vectors, i and j are always external iterators, etc. These
+bindings are used in the STL_TEST - if you need a vector and an int, have
+parameters a and n.
+
+There is a list (BOOST_PP_SEQ) of test types and interface types. If the
+type passes the restriction, then the test body is instantiated with that
+type as its template parameter. Instantiation ensures that the contractual
+elements of the standard are satisfied.  Only the test types, however, and
+not the interfact types, are actually tested.
+
+If a test type passes the restriction, then it is run with a variety of
+arguments. Each variable (e.g. a and b) have a generator, which generates
+a range of values for that variable before each test. Generated values are not
+reused - they are remade for every run. This causes long runtimes, but ensures
+that corner cases are not missed.
+
+There are two implicit extra parameters, z and ticks. Ignore z. Ticks, on the
+other hand, is very important. Each is test is run multiple times with the
+same arguments; the first time with no ticks (and hence no Data or Alloc
+exceptions), and then once again for each and every location that an
+exception can be thrown. This ensures that exception corner cases are alse
+thoroughly tested.
+
+At the end of each test, a set of verification functions is run to ensure
+that nothing was corrupted.
+
+---------
+The tests
+
+All specifications from N3337 Chapter 23 (Containers) that pertains to
+vector is tested (if possible). Each aspect has a dedicated STL_TEST, so that
+there are no compounding errors. The tests are organized as they appear in
+N3337.
+
+The backbone of the testing framework is based on a small set of vector
+operations:
+-empty construction
+-copy construction (a little bit)
+-size
+-capacity
+-data
+-emplace_back
+
+These functions are used to generate and verify the tests. If they fail, then
+the cascade of errors will be enormous. They are, therefore, tested first.
+
+*/
+/*
+
+THOUGHTS:
+
+-Not all complexity checks are verified. These will be relentlessly hunted down
+ in the benchmarking phase.
+
+-It seems that initializer lists with implicit arguments are constructed before
+ being passed into the vector. When one of the constructors fails, it fails in
+ the initializer list, before it even gets to the vector. The IL, however,
+ doesn't clean up properly, and already-constructed elements are not
+ destroyed. This causes a memory leak, and the tests break, but it is not the
+ fault of the vector itself. Further, since the implementation for the
+ initializer lists is specified in the standard as calling an associated
+ function with (il.begin(), il.end()), we really just have to check the throws
+ cases for the associated functions (which all work fine). Initializer lists
+ also do not work with explicit constructors.
+
+-The implementation of std::copy from iterators prevents Data(int) from being
+ explicit. Explicitness is, perhaps, a desirable quality, but with fundamental
+ std library code like copy not supporting it, it seems impractical.
+
+*/
+
+// include the vector first, to ensure its header is self-sufficient
+#ifdef USING_STD_VECTOR
+#include <vector>
+#define VECTOR_ std::vector
+#else
+#include <folly/FBVector.h>
+#define VECTOR_ folly::fbvector
+#endif
+
+//#define USING_STD_VECTOR
+
+#include <iostream>
+#include <sstream>
+#include <typeinfo>
+#include <type_traits>
+#include <map>
+#include <set>
+#include <string>
+#include <stdexcept>
+#include <exception>
+#include <climits>
+#include <cstddef>
+#include <iomanip>
+
+#include <folly/ScopeGuard.h>
+#include <folly/Conv.h>
+#include <boost/preprocessor.hpp>
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace folly;
+
+//=============================================================================
+//=============================================================================
+// Data type
+
+//-----------------------------------------------------------------------------
+// Flags
+
+typedef uint32_t Flags;
+
+// each method has 3 options: normal, noexcept, throw, and deleted
+// normal is the default
+// throw is mutually exclusive with noexcept
+//
+// DC - default constructor
+// CC - copy constructor
+// MC - move constructor
+// OC - other constructor
+// CA - copy assignment
+// MA - move assignment
+enum FlagVals : Flags {
+  DC_NOEXCEPT = 0x1,
+  DC_THROW    = 0x2,
+  DC_DELETE   = 0x8000,
+  CC_NOEXCEPT = 0x4,
+  CC_THROW    = 0x8,
+  CC_DELETE   = 0x10000,
+  MC_NOEXCEPT = 0x10,
+  MC_THROW    = 0x20,
+  MC_DELETE   = 0x20000,
+  OC_NOEXCEPT = 0x40,
+  OC_THROW    = 0x80,
+  // OC_DELETE - DNE
+
+  CA_NOEXCEPT = 0x100,
+  CA_THROW    = 0x200,
+  CA_DELETE   = 0x40000,
+  MA_NOEXCEPT = 0x400,
+  MA_THROW    = 0x800,
+  MA_DELETE   = 0x80000,
+
+  ALL_DELETE  = DC_DELETE | CC_DELETE | MC_DELETE
+              | CA_DELETE | MA_DELETE,
+
+  IS_RELOCATABLE
+              = 0x2000,
+
+  // for the allocator
+  PROP_COPY = 0x100000,
+  PROP_MOVE = 0x200000,
+  PROP_SWAP = 0x400000,
+};
+
+//-----------------------------------------------------------------------------
+// Deletors
+
+template <bool b> struct D0 {
+  D0() = default;
+  D0(const D0&) = default;
+  D0(D0&&) = default;
+  explicit D0(std::nullptr_t) {}
+  D0& operator=(const D0&) = default;
+  D0& operator=(D0&&) = default;
+};
+template <> struct D0<true> {
+  D0() = delete;
+  D0(const D0&) = default;
+  D0(D0&&) = default;
+  explicit D0(std::nullptr_t) {}
+  D0& operator=(const D0&) = default;
+  D0& operator=(D0&&) = default;
+};
+
+template <bool b> struct D1 {
+  D1() = default;
+  D1(const D1&) = default;
+  D1(D1&&) = default;
+  explicit D1(std::nullptr_t) {}
+  D1& operator=(const D1&) = default;
+  D1& operator=(D1&&) = default;
+};
+template <> struct D1<true> {
+  D1() = default;
+  D1(const D1&) = delete;
+  D1(D1&&) = default;
+  explicit D1(std::nullptr_t) {}
+  D1& operator=(const D1&) = default;
+  D1& operator=(D1&&) = default;
+};
+
+template <bool b> struct D2 {
+  D2() = default;
+  D2(const D2&) = default;
+  D2(D2&&) = default;
+  explicit D2(std::nullptr_t) {}
+  D2& operator=(const D2&) = default;
+  D2& operator=(D2&&) = default;
+};
+template <> struct D2<true> {
+  D2() = default;
+  D2(const D2&) = default;
+  D2(D2&&) = delete;
+  explicit D2(std::nullptr_t) {}
+  D2& operator=(const D2&) = default;
+  D2& operator=(D2&&) = default;
+};
+
+template <bool b> struct D3 {
+  D3() = default;
+  D3(const D3&) = default;
+  D3(D3&&) = default;
+  explicit D3(std::nullptr_t) {}
+  D3& operator=(const D3&) = default;
+  D3& operator=(D3&&) = default;
+};
+template <> struct D3<true> {
+  D3() = default;
+  D3(const D3&) = default;
+  D3(D3&&) = default;
+  explicit D3(std::nullptr_t) {}
+  D3& operator=(const D3&) = delete;
+  D3& operator=(D3&&) = default;
+};
+
+template <bool b> struct D4 {
+  D4() = default;
+  D4(const D4&) = default;
+  D4(D4&&) = default;
+  explicit D4(std::nullptr_t) {}
+  D4& operator=(const D4&) = default;
+  D4& operator=(D4&&) = default;
+};
+template <> struct D4<true> {
+  D4() = default;
+  D4(const D4&) = default;
+  D4(D4&&) = default;
+  explicit D4(std::nullptr_t) {}
+  D4& operator=(const D4&) = default;
+  D4& operator=(D4&&) = delete;
+};
+
+template <Flags f>
+struct Delete : D0<f & DC_DELETE>
+              , D1<f & CC_DELETE>
+              , D2<f & MC_DELETE>
+              , D3<f & CA_DELETE>
+              , D4<f & MA_DELETE> {
+  Delete() = default;
+  Delete(const Delete&) = default;
+  Delete(Delete&&) = default;
+  Delete& operator=(const Delete&) = default;
+  Delete& operator=(Delete&&) = default;
+
+  explicit Delete(std::nullptr_t)
+      : D0<f & DC_DELETE>(nullptr)
+      , D1<f & CC_DELETE>(nullptr)
+      , D2<f & MC_DELETE>(nullptr)
+      , D3<f & CA_DELETE>(nullptr)
+      , D4<f & MA_DELETE>(nullptr)
+      {}
+};
+
+//-----------------------------------------------------------------------------
+// Ticker
+
+struct TickException : std::runtime_error {
+  explicit TickException(const std::string& s)
+    : std::runtime_error("tick: " + s) {}
+};
+
+struct Ticker {
+  static int CountTicks;
+  static int TicksLeft;
+  static void Tick(const std::string& s) {
+    if (TicksLeft == 0) throw TickException(s);
+    CountTicks++;
+    TicksLeft--;
+  }
+};
+
+int Ticker::CountTicks = 0;
+int Ticker::TicksLeft = -1;
+
+template <Flags f>
+struct DataTicker : Ticker {
+  DataTicker() noexcept(f & DC_NOEXCEPT) {
+    if (!(f & DC_NOEXCEPT)) Tick("Data()");
+  }
+  DataTicker(const DataTicker&) noexcept(f & CC_NOEXCEPT) {
+    if (!(f & CC_NOEXCEPT)) Tick("Data(const Data&)");
+  }
+  DataTicker(DataTicker&&) noexcept(f & MC_NOEXCEPT) {
+    if (!(f & MC_NOEXCEPT)) Tick("Data(Data&&)");
+  }
+  explicit DataTicker(std::nullptr_t) noexcept(f & OC_NOEXCEPT) {
+    if (!(f & OC_NOEXCEPT)) Tick("Data(int)");
+  }
+  ~DataTicker() noexcept {}
+  void operator=(const DataTicker&) noexcept(f & CA_NOEXCEPT) {
+    if (!(f & CA_NOEXCEPT)) Tick("op=(const Data&)");
+  }
+  void operator=(DataTicker&&) noexcept(f & MA_NOEXCEPT) {
+    if (!(f & MA_NOEXCEPT)) Tick("op=(Data&&)");
+  }
+};
+
+//-----------------------------------------------------------------------------
+// Operation counter
+
+struct Counter {
+  static int CountDC, CountCC, CountMC, CountOC, CountCA, CountMA;
+  static int CountDestroy, CountTotalOps, CountLoggedConstruction;
+
+  Counter()                         noexcept { CountTotalOps++; CountDC++; }
+  Counter(const Counter&)           noexcept { CountTotalOps++; CountCC++; }
+  Counter(Counter&&)                noexcept { CountTotalOps++; CountMC++; }
+  explicit Counter(std::nullptr_t)  noexcept { CountTotalOps++; CountOC++; }
+  void operator=(const Counter&)    noexcept { CountTotalOps++; CountCA++; }
+  void operator=(Counter&&)         noexcept { CountTotalOps++; CountMA++; }
+  ~Counter()                      noexcept { CountTotalOps++; CountDestroy++; }
+};
+
+int Counter::CountDC = 0;
+int Counter::CountCC = 0;
+int Counter::CountMC = 0;
+int Counter::CountOC = 0;
+int Counter::CountCA = 0;
+int Counter::CountMA = 0;
+int Counter::CountDestroy = 0;
+int Counter::CountTotalOps = 0;
+int Counter::CountLoggedConstruction = 0;
+
+//-----------------------------------------------------------------------------
+// Tracker
+
+struct Tracker {
+  static int UID;
+  static std::map<int, int> UIDCount;
+  static int UIDTotal;
+  static std::map<const Tracker*, int> Locations;
+  static bool Print;
+
+  Tracker* self;
+  int uid;
+
+  Tracker(Tracker* self, int uid) : self(self), uid(uid) {}
+};
+
+template <bool isRelocatable>
+struct DataTracker : Tracker {
+  DataTracker() noexcept : Tracker(this, UID++) {
+    UIDCount[uid]++;
+    UIDTotal++;
+    if (!isRelocatable) Locations[self] = uid;
+    print("Data()");
+  }
+  DataTracker(const DataTracker& o) noexcept : Tracker(this, o.uid) {
+    UIDCount[uid]++;
+    UIDTotal++;
+    if (!isRelocatable) Locations[self] = uid;
+    print("Data(const Data&)");
+  }
+  DataTracker(DataTracker&& o) noexcept : Tracker(this, o.uid) {
+    UIDCount[uid]++;
+    UIDTotal++;
+    if (!isRelocatable) Locations[self] = uid;
+    print("Data(Data&&)");
+  }
+
+  explicit DataTracker(int uid) noexcept : Tracker(this, uid) {
+    UIDCount[uid]++;
+    UIDTotal++;
+    if (!isRelocatable) Locations[self] = uid;
+    print("Data(int)");
+  }
+
+  ~DataTracker() noexcept {
+    UIDCount[uid]--;
+    UIDTotal--;
+    if (!isRelocatable) Locations.erase(self);
+    print("~Data()");
+    uid = 0xdeadbeef;
+    self = (DataTracker*)0xfeebdaed;
+  }
+
+  DataTracker& operator=(const DataTracker& o) noexcept {
+    UIDCount[uid]--;
+    uid = o.uid;
+    UIDCount[uid]++;
+    if (!isRelocatable) Locations[self] = uid;
+    print("op=(const Data&)");
+    return *this;
+  }
+  DataTracker& operator=(DataTracker&& o) noexcept {
+    UIDCount[uid]--;
+    uid = o.uid;
+    UIDCount[uid]++;
+    if (!isRelocatable) Locations[self] = uid;
+    print("op=(Data&&)");
+    return *this;
+  }
+
+  void print(const std::string& fun) {
+    if (Print) {
+      std::cerr << std::setw(20) << fun << ": uid = " << std::setw(3) << uid;
+      if (!isRelocatable) std::cerr << ", self = " << self;
+      std::cerr << std::endl;
+    }
+  }
+};
+
+int Tracker::UID = 1234;
+std::map<int, int> Tracker::UIDCount;
+int Tracker::UIDTotal = 0;
+std::map<const Tracker*, int> Tracker::Locations;
+bool Tracker::Print = false;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// Data
+
+template <Flags f = 0, size_t pad = 0>
+struct Data : DataTracker<f & IS_RELOCATABLE>,
+              Counter, DataTicker<f>, Delete<f> {
+  static const Flags flags = f;
+  char spacehog[pad ? pad : 1];
+
+  Data() = default;
+  Data(const Data&) = default;
+  Data(Data&&) = default;
+  /* implicit */ Data(int i)
+    : DataTracker<f & IS_RELOCATABLE>(i), Counter()
+    , DataTicker<f>(nullptr)
+    , Delete<f>(nullptr)
+  {}
+  ~Data() = default;
+  Data& operator=(const Data&) = default;
+  Data& operator=(Data&&) = default;
+
+private:
+  int operator&() const;
+};
+
+namespace folly {
+template <Flags f, size_t pad>
+struct IsRelocatable<Data<f, pad>>
+  : std::integral_constant<bool,
+      f & IS_RELOCATABLE
+    > {};
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// Allocator
+
+template <typename T>
+struct isPropCopy : true_type {};
+template <Flags f, size_t pad>
+struct isPropCopy<Data<f, pad>> :
+  std::integral_constant<bool, f & PROP_COPY> {};
+
+template <typename T>
+struct isPropMove : true_type {};
+template <Flags f, size_t pad>
+struct isPropMove<Data<f, pad>> :
+  std::integral_constant<bool, f & PROP_MOVE> {};
+
+template <typename T>
+struct isPropSwap : true_type {};
+template <Flags f, size_t pad>
+struct isPropSwap<Data<f, pad>> :
+  std::integral_constant<bool, f & PROP_SWAP> {};
+
+
+struct AllocTracker {
+  static int Constructed;
+  static int Destroyed;
+  static map<void*, size_t> Allocated;
+  static map<void*, int> Owner;
+};
+int AllocTracker::Constructed = 0;
+int AllocTracker::Destroyed = 0;
+map<void*, size_t> AllocTracker::Allocated;
+map<void*, int> AllocTracker::Owner;
+
+template <class T>
+struct Alloc : AllocTracker, Ticker {
+  typedef typename std::allocator<T>::pointer pointer;
+  typedef typename std::allocator<T>::const_pointer const_pointer;
+  typedef typename std::allocator<T>::size_type size_type;
+  typedef typename std::allocator<T>::value_type value_type;
+
+  //-----
+  // impl
+
+  std::allocator<T> a;
+  int id;
+  explicit Alloc(int i = 8) : a(a), id(i) {}
+  Alloc(const Alloc& o) : a(o.a), id(o.id) {}
+  Alloc(Alloc&& o) : a(move(o.a)), id(o.id) {}
+  Alloc& operator=(const Alloc&) = default;
+  Alloc& operator=(Alloc&&) = default;
+  bool operator==(const Alloc& o) const { return a == o.a && id == o.id; }
+  bool operator!=(const Alloc& o) const { return !(*this == o); }
+
+  //---------
+  // tracking
+
+  pointer allocate(size_type n) {
+    if (n == 0) {
+      cerr << "called allocate(0)" << endl;
+      throw runtime_error("allocate fail");
+    }
+    Tick("allocate");
+    auto p = a.allocate(n);
+    Allocated[p] = n;
+    Owner[p] = id;
+    return p;
+  }
+
+  void deallocate(pointer p, size_type n) {
+    if (p == nullptr) {
+      cerr << "deallocate(nullptr, " << n << ")" << endl;
+      FAIL() << "deallocate failed";
+    }
+    if (Allocated[p] != n) {
+      cerr << "deallocate(" << p << ", " << n << ") invalid: ";
+      if (Allocated[p] == 0) cerr << "never allocated";
+      else if (Allocated[p] == -1) cerr << "already deallocated";
+      else cerr << "wrong number (want " << Allocated[p] << ")";
+      cerr << endl;
+      FAIL() << "deallocate failed";
+    }
+    if (Owner[p] != id) {
+      cerr << "deallocate(" << p << "), where pointer is owned by "
+           << Owner[p] << ", instead of self - " << id << endl;
+      FAIL() << "deallocate failed";
+    }
+    Allocated[p] = -1;
+    a.deallocate(p, n);
+  }
+
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    Tick("construct");
+    a.construct(p, std::forward<Args>(args)...);
+    Constructed++;
+  }
+
+  template <class U>
+  void destroy(U* p) {
+    Destroyed++;
+    a.destroy(p);
+  }
+
+  //--------------
+  // container ops
+
+  Alloc select_on_container_copy_construction() const {
+    Tick("select allocator for copy");
+    return Alloc(id + 1);
+  }
+
+  typedef isPropCopy<T> propagate_on_container_copy_assignment;
+  typedef isPropMove<T> propagate_on_container_move_assignment;
+  typedef isPropSwap<T> propagate_on_container_swap;
+};
+
+//=============================================================================
+//=============================================================================
+// Verification and resetting
+
+void softReset(int ticks = -1) {
+  Counter::CountLoggedConstruction +=
+    Counter::CountDC + Counter::CountCC + Counter::CountMC
+    + Counter::CountOC - Counter::CountDestroy;
+  Counter::CountDC = Counter::CountCC = Counter::CountMC
+    = Counter::CountOC = Counter::CountCA = Counter::CountMA = 0;
+  Counter::CountDestroy = Counter::CountTotalOps = 0;
+  Ticker::CountTicks = 0;
+  Ticker::TicksLeft = ticks;
+}
+
+void hardReset() {
+  Tracker::UIDCount.clear();
+  Tracker::UIDTotal = 0;
+  Tracker::Locations.clear();
+  softReset();
+  Counter::CountLoggedConstruction = 0;
+
+  AllocTracker::Constructed = 0;
+  AllocTracker::Destroyed = 0;
+  AllocTracker::Allocated.clear();
+  AllocTracker::Owner.clear();
+}
+
+int getTotal() {
+  int con = Counter::CountDC + Counter::CountCC
+          + Counter::CountMC + Counter::CountOC
+          + Counter::CountLoggedConstruction;
+  int del = Counter::CountDestroy;
+  return con - del;
+}
+
+void isSane() {
+  int tot = getTotal();
+  ASSERT_GE(tot, 0) << "more objects deleted than constructed";
+
+  ASSERT_EQ(tot, Tracker::UIDTotal)
+    << "UIDTotal has incorrect number of objects";
+
+  int altTot = 0;
+  for (const auto& kv : Tracker::UIDCount) {
+    ASSERT_TRUE(kv.second >= 0) << "there exists " << kv.second << " Data "
+      "with uid " << kv.first;
+    altTot += kv.second;
+  }
+  ASSERT_EQ(tot, altTot) << "UIDCount corrupted";
+
+  if (!Tracker::Locations.empty()) { // implied by IsRelocatable
+    ASSERT_EQ(tot, Tracker::Locations.size())
+      << "Locations has incorrect number of objects";
+    for (const auto& du : Tracker::Locations) {
+      ASSERT_EQ(du.second, du.first->uid) << "Locations contains wrong uid";
+      ASSERT_EQ(du.first, du.first->self) << "Data.self is corrupted";
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Traits
+
+template <typename T>
+struct is_copy_constructibleAndAssignable
+  : std::integral_constant<bool,
+      std::is_copy_constructible<T>::value &&
+      std::is_copy_assignable<T>::value
+    > {};
+
+template <typename T>
+struct is_move_constructibleAndAssignable
+  : std::integral_constant<bool,
+      std::is_move_constructible<T>::value &&
+      std::is_move_assignable<T>::value
+    > {};
+
+template <class Vector>
+struct customAllocator
+  : std::integral_constant<bool,
+      !is_same<
+        typename Vector::allocator_type,
+        std::allocator<typename Vector::value_type>
+      >::value
+    > {};
+
+template <typename T>
+struct special_move_assignable
+  : is_move_constructibleAndAssignable<T> {};
+template <Flags f, size_t pad>
+struct special_move_assignable<Data<f, pad>>
+  : std::integral_constant<bool,
+      is_move_constructibleAndAssignable<Data<f, pad>>::value ||
+      f & PROP_MOVE
+    > {};
+
+//=============================================================================
+//=============================================================================
+// Framework
+
+//-----------------------------------------------------------------------------
+// Timing
+
+uint64_t ReadTSC() {
+   unsigned reslo, reshi;
+
+    __asm__ __volatile__  (
+    "xorl %%eax,%%eax \n cpuid \n"
+     ::: "%eax", "%ebx", "%ecx", "%edx");
+    __asm__ __volatile__  (
+    "rdtsc\n"
+     : "=a" (reslo), "=d" (reshi) );
+    __asm__ __volatile__  (
+    "xorl %%eax,%%eax \n cpuid \n"
+     ::: "%eax", "%ebx", "%ecx", "%edx");
+
+   return ((uint64_t)reshi << 32) | reslo;
+}
+
+//-----------------------------------------------------------------------------
+// New Boost
+
+#define IBOOST_PP_VARIADIC_SIZE(...) IBOOST_PP_VARIADIC_SIZE_I(__VA_ARGS__,   \
+  64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, \
+  45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, \
+  26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8,   \
+  7, 6, 5, 4, 3, 2, 1,)
+#define IBOOST_PP_VARIADIC_SIZE_I(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,     \
+  e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24,  \
+  e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36, e37, e38, e39,  \
+  e40, e41, e42, e43, e44, e45, e46, e47, e48, e49, e50, e51, e52, e53, e54,  \
+  e55, e56, e57, e58, e59, e60, e61, e62, e63, size, ...) size
+#define IBOOST_PP_VARIADIC_TO_SEQ(args...) \
+  BOOST_PP_TUPLE_TO_SEQ(IBOOST_PP_VARIADIC_SIZE(args), (args))
+
+//-----------------------------------------------------------------------------
+// STL_TEST
+
+#define GEN_TEST(r, name, type)                                   \
+  {                                                               \
+    string atype = PrettyType<typename type::allocator_type>()(); \
+    string ptype = PrettyType<typename type::value_type>()();     \
+    SCOPED_TRACE("allocator: " + atype); {                        \
+    SCOPED_TRACE("datatype: " + ptype); {                         \
+    test_ ## name ## 3 <type> ();                                 \
+    if (::testing::Test::HasFatalFailure()) return;               \
+  }}}
+#define GEN_TYPE_TEST(r, name, type) \
+  if (0) test_I_ ## name ## 3 <type> ();
+#define GEN_RUNNABLE_TEST(r, name, type) \
+  one = test_I_ ## name ## 3 <type> () || one;
+
+#define GEN_LOOPER(r, d, arg) BOOST_PP_CAT(LOOPER_, arg)
+#define GEN_VMAKER(r, d, arg) { BOOST_PP_CAT(VMAKER_, arg) {
+#define GEN_UMAKER(r, d, arg) } BOOST_PP_CAT(UMAKER_, arg) }
+#define GEN_CLOSER(r, d, arg) BOOST_PP_CAT(CLOSER_, arg)
+
+#define TYPIFY(r, d, name) BOOST_PP_CAT(TYPIFY_, name)
+#define ARGIFY(r, d, name) TYPIFY(r, d, name) name
+
+#define MAKE_TEST(ref, name, types, restriction, argseq, rawargs...)     \
+  template <class Vector> void test_ ## name ## 2 (std::false_type) {}   \
+  template <class Vector> void test_ ## name ## 2 (std::true_type) {     \
+    BOOST_PP_SEQ_FOR_EACH(GEN_LOOPER, _, argseq)                         \
+    { SETUP {                                                            \
+    BOOST_PP_SEQ_FOR_EACH(GEN_VMAKER, _, argseq)                         \
+    {                                                                    \
+    test_ ## name <Vector, typename Vector::value_type,                  \
+      typename Vector::allocator_type> ( rawargs );                      \
+    if (::testing::Test::HasFatalFailure()) return;                      \
+    }                                                                    \
+    BOOST_PP_SEQ_FOR_EACH(GEN_UMAKER, _, BOOST_PP_SEQ_REVERSE(argseq))   \
+    } TEARDOWN }                                                         \
+    BOOST_PP_SEQ_FOR_EACH(GEN_CLOSER, _, BOOST_PP_SEQ_REVERSE(argseq))   \
+  }                                                                      \
+  template <class Vector> void test_ ## name ## 3 () {                   \
+    test_ ## name ## 2 <Vector> (std::integral_constant<bool,            \
+        restriction<typename Vector::value_type>::value &&               \
+        is_copy_constructible<typename Vector::value_type>::value        \
+      >());                                                              \
+  }                                                                      \
+                                                                         \
+  template <class Vector> bool test_I_ ## name ## 2 (std::false_type)    \
+    { return false; }                                                    \
+  template <class Vector> bool test_I_ ## name ## 2 (std::true_type) {   \
+    return true;                                                         \
+    auto f = test_ ## name <Vector,                                      \
+      typename Vector::value_type, typename Vector::allocator_type>;     \
+    return true;                                                         \
+  }                                                                      \
+  template <class Vector> bool test_I_ ## name ## 3 () {                 \
+    return test_I_ ## name ## 2 <Vector> (std::integral_constant<bool,   \
+      restriction<typename Vector::value_type>::value>());               \
+    return false;                                                        \
+  }                                                                      \
+                                                                         \
+  TEST(FBVector, name) {                                                 \
+    SCOPED_TRACE("N3337 reference: " ref);                               \
+    BOOST_PP_SEQ_FOR_EACH(GEN_TEST, name, types)                         \
+    BOOST_PP_SEQ_FOR_EACH(GEN_TYPE_TEST, name, INTERFACE_TYPES)          \
+    bool one = false;                                                    \
+    BOOST_PP_SEQ_FOR_EACH(GEN_RUNNABLE_TEST, name, types)                \
+    if (!one) FAIL() << "No tests qualified to run";                     \
+  }
+
+#define DECL(name, args...)                                                   \
+  template <class Vector, typename T, typename Allocator>                     \
+  void test_ ## name (BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(               \
+    ARGIFY, _, IBOOST_PP_VARIADIC_TO_SEQ(args))))
+
+#define STL_TEST_I(ref, name, restriction, args...)                           \
+  DECL(name, args);                                                           \
+  MAKE_TEST(ref, name, TEST_TYPES, restriction,                               \
+    IBOOST_PP_VARIADIC_TO_SEQ(args), args)                                    \
+  DECL(name, args)
+
+#define STL_TEST(ref, name, restriction, args...) \
+  STL_TEST_I(ref, name, restriction, z, ## args, ticks)
+
+//-----------------------------------------------------------------------------
+// Test Types
+
+typedef Data<> ED1;
+typedef Data<0, 4080> ED2;
+typedef Data<MC_NOEXCEPT> ED3;
+typedef Data<MC_NOEXCEPT | CC_DELETE> ED4;
+typedef Data<IS_RELOCATABLE> ED5;
+
+typedef VECTOR_<int, std::allocator<int>> _TVIS;
+typedef VECTOR_<int, Alloc<int>> _TVI;
+typedef VECTOR_<ED1, std::allocator<ED1>> _TV1;
+typedef VECTOR_<ED2, std::allocator<ED2>> _TV2;
+typedef VECTOR_<ED3, std::allocator<ED3>> _TV3;
+typedef VECTOR_<ED4, std::allocator<ED4>> _TV4;
+typedef VECTOR_<ED5, std::allocator<ED5>> _TV5v1;
+typedef VECTOR_<ED5, Alloc<ED5>> _TV5;
+
+typedef Data<PROP_COPY> EP1;
+typedef Data<PROP_MOVE> EP2;
+typedef Data<PROP_SWAP> EP3;
+
+typedef VECTOR_<EP1, Alloc<EP1>> _TP1;
+typedef VECTOR_<EP2, Alloc<EP2>> _TP2;
+typedef VECTOR_<EP3, Alloc<EP3>> _TP3;
+
+#define TEST_TYPES (_TVIS)(_TVI)(_TV1)(_TV2)(_TV3)(_TV4)(_TV5v1)(_TV5) \
+  (_TP1)(_TP2)(_TP3)
+
+typedef Data<ALL_DELETE> DD1; // unoperable
+typedef Data<DC_DELETE | CC_DELETE | MC_DELETE> DD2; // unconstructible
+typedef Data<CA_DELETE | MA_DELETE> DD3; // unassignable
+typedef Data<CC_DELETE | MC_DELETE> DD4; // uncopyable
+typedef Data<ALL_DELETE & ~DC_DELETE> DD5; // only default constructible
+typedef Data<CC_DELETE> DD6; // move-only copy construction
+typedef Data<CA_DELETE> DD7; // move-only assignment
+
+typedef Data<ALL_DELETE | PROP_MOVE> DDSMA;
+typedef VECTOR_<DDSMA, Alloc<DDSMA>> _TSpecialMA;
+
+#define INTERFACE_TYPES \
+  (_TVI)(VECTOR_<DD1>)(VECTOR_<DD2>)(VECTOR_<DD3>) \
+  (VECTOR_<DD4>)(VECTOR_<DD5>)(VECTOR_<DD6>) \
+  (VECTOR_<DD7>)(_TSpecialMA)
+
+//-----------------------------------------------------------------------------
+// Pretty printers
+
+template <typename T>
+struct PrettyType {
+  string operator()() {
+    if (is_same<T, int>::value) return "int";
+    if (is_same<T, char>::value) return "char";
+    if (is_same<T, uint64_t>::value) return "uint64_t";
+    return typeid(T).name();
+  }
+};
+
+template <Flags f, size_t pad>
+struct PrettyType<Data<f, pad>> {
+  string operator()() {
+    stringstream tpe;
+    tpe << "Data";
+
+    if ((f & DC_DELETE) ||
+        (f & CC_DELETE) ||
+        (f & MC_DELETE) ||
+        (f & CA_DELETE) ||
+        (f & MA_DELETE)) {
+      tpe << "[^";
+      if (f & DC_DELETE) tpe << " DC,";
+      if (f & CC_DELETE) tpe << " CC,";
+      if (f & MC_DELETE) tpe << " MC,";
+      if (f & CA_DELETE) tpe << " CA,";
+      if (f & MA_DELETE) tpe << " MA,";
+      tpe << "]";
+    }
+
+    if ((f & DC_NOEXCEPT) ||
+        (f & CC_NOEXCEPT) ||
+        (f & MC_NOEXCEPT) ||
+        (f & CA_NOEXCEPT) ||
+        (f & MA_NOEXCEPT)) {
+      tpe << "[safe";
+      if (f & DC_NOEXCEPT) tpe << " DC,";
+      if (f & CC_NOEXCEPT) tpe << " CC,";
+      if (f & MC_NOEXCEPT) tpe << " MC,";
+      if (f & CA_NOEXCEPT) tpe << " CA,";
+      if (f & MA_NOEXCEPT) tpe << " MA,";
+      tpe << "]";
+    }
+
+    if (f & IS_RELOCATABLE) {
+      tpe << "(relocatable)";
+    }
+
+    if (pad != 0) {
+      tpe << "{pad " << pad << "}";
+    }
+
+    return tpe.str();
+  }
+};
+
+template <typename T>
+struct PrettyType<std::allocator<T>> {
+  string operator()() {
+    return "std::allocator<" + PrettyType<T>()() + ">";
+  }
+};
+
+template <typename T>
+struct PrettyType<Alloc<T>> {
+  string operator()() {
+    return "Alloc<" + PrettyType<T>()() + ">";
+  }
+};
+
+//-----------------------------------------------------------------------------
+// Setup, teardown, runup, rundown
+
+// These four macros are run once per test. Setup and runup occur before the
+// test, teardown and rundown after. Setup and runup straddle the
+// initialization sequence, whereas rundown and teardown straddle the
+// cleanup.
+
+#define SETUP hardReset();
+#define TEARDOWN
+
+//-----------------------------------------------------------------------------
+// Types and typegens
+
+//------
+// dummy
+
+#define TYPIFY_z std::nullptr_t
+#define LOOPER_z                                 \
+  Vector* a_p = nullptr; Vector* b_p = nullptr;  \
+  typename Vector::value_type* t_p = nullptr;
+#define VMAKER_z std::nullptr_t z = nullptr;
+#define UMAKER_z                                                      \
+  verify<Vector>(0);                                                  \
+  if (::testing::Test::HasFatalFailure()) return;
+#define CLOSER_z
+
+//------
+// ticks
+
+#define VERIFICATION                                        \
+  if (b_p != nullptr) verify(t_p != nullptr ,*a_p, *b_p);   \
+  else if (a_p != nullptr) verify(t_p != nullptr, *a_p);    \
+  else verify<Vector>(t_p != nullptr);                      \
+  if (::testing::Test::HasFatalFailure()) return;
+
+#define TYPIFY_ticks int
+#define LOOPER_ticks          \
+  int _maxTicks_ = 0;         \
+  bool ticks_thrown = false;  \
+  for (int ticks = -1; ticks < _maxTicks_; ++ticks) {
+#define VMAKER_ticks                                        \
+  string ticks_st = folly::to<string>("ticks = ", ticks);   \
+  SCOPED_TRACE(ticks_st);                                   \
+  { SCOPED_TRACE("pre-run verification");                   \
+    VERIFICATION }                                          \
+  try {                                                     \
+    softReset(ticks);
+#define UMAKER_ticks _maxTicks_ = Ticker::CountTicks; }           \
+  catch (const TickException&) { ticks_thrown = true; }           \
+  catch (const std::exception& e)                                 \
+    { FAIL() << "EXCEPTION: " << e.what(); }                      \
+  catch (...)                                                     \
+    { FAIL() << "UNKNOWN EXCEPTION"; }                            \
+  if (ticks >= 0 && Ticker::CountTicks > ticks && !ticks_thrown)  \
+    FAIL() << "CountTicks = " << Ticker::CountTicks << " > "      \
+           << ticks << " = ticks"                                 \
+           << ", but no tick error was observed";                 \
+  VERIFICATION
+#define CLOSER_ticks }
+
+
+//--------------------------------------------------
+// vectors (second could be .equal, ==, or distinct)
+
+static const vector<pair<int, int>> VectorSizes = {
+  {  0, -1},
+  {  1, -1},
+  {  2, -1},
+  { 10, -1}, { 10, 1}, { 10, 0},
+  {100, -1}, {100, 1},
+
+  //{   10, -1}, {   10, 0}, {   10, 1}, {   10, 2}, {   10, 10},
+  //{  100, -1}, {  100, 0}, {  100, 1}, {  100, 2}, {  100, 10}, {  100, 100},
+  //{ 1000, -1}, { 1000, 0}, { 1000, 1}, { 1000, 2}, { 1000, 10}, { 1000, 100},
+  //  { 1000, 1000},
+};
+
+int populateIndex = 1426;
+template <class Vector>
+void populate(Vector& v, const pair<int, int>& ss) {
+  int i = 0;
+  for (; i < ss.first; ++i) {
+    v.emplace_back(populateIndex++);
+  }
+  if (ss.second >= 0) {
+    while (v.capacity() - v.size() != ss.second) {
+      v.emplace_back(populateIndex++);
+    }
+  }
+}
+
+template <typename A>
+struct allocGen {
+  static A get() { return A(); }
+};
+template <typename T>
+struct allocGen<Alloc<T>> {
+  static Alloc<T> get() {
+    static int c = 0;
+    c += 854;
+    return Alloc<T>(c);
+  }
+};
+
+#define TYPIFY_a Vector&
+#define LOOPER_a for (const auto& a_ss : VectorSizes) {
+#define VMAKER_a                                                            \
+  Vector a(allocGen<typename Vector::allocator_type>::get());               \
+  a_p = &a;                                                                 \
+  populate(*a_p, a_ss);                                                     \
+  string a_st = folly::to<string>("a (", a.size(), "/", a.capacity(), ")"); \
+  SCOPED_TRACE(a_st);
+#define UMAKER_a verify(0, a); if (::testing::Test::HasFatalFailure()) return;
+#define CLOSER_a }
+
+#define TYPIFY_b Vector&
+#define LOOPER_b for (int b_i = -2; b_i < (int)VectorSizes.size(); ++b_i) {
+#define VMAKER_b                                                            \
+  Vector b_s(allocGen<typename Vector::allocator_type>::get());             \
+  b_p = &b_s; string b_st;                                                  \
+  if (b_i == -2) {                                                          \
+    b_p = &a;                                                               \
+    b_st = "b is an alias of a";                                            \
+  }                                                                         \
+  else if (b_i == -1) {                                                     \
+    b_s.~Vector();                                                          \
+    new (&b_s) Vector(a);                                                   \
+    b_st = "b is a deep copy of a";                                         \
+  }                                                                         \
+  else {                                                                    \
+    populate(b_s, VectorSizes[b_i]);                                        \
+    b_st = folly::to<string>("b (", b_s.size(), "/", b_s.capacity(), ")");  \
+  }                                                                         \
+  Vector& b = *b_p;                                                         \
+  SCOPED_TRACE(b_st);
+#define UMAKER_b \
+  verify(0, a, b); if (::testing::Test::HasFatalFailure()) return;
+#define CLOSER_b }
+
+//----
+// int
+
+static const vector<int> nSizes = { 0, 1, 2, 9, 10, 11 };
+
+#define TYPIFY_n int
+#define LOOPER_n for (int n : nSizes) {
+#define VMAKER_n \
+  string n_st = folly::to<string>("n = ", n); SCOPED_TRACE(n_st);
+#define UMAKER_n
+#define CLOSER_n }
+
+//-----------------------
+// non-internal iterators
+
+static int ijarr[12] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
+static int ijarC[12] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
+
+#define TYPIFY_i int*
+#define LOOPER_i
+#define VMAKER_i int* i = ijarr; SCOPED_TRACE("i = fib[0]");
+#define UMAKER_i
+#define CLOSER_i
+
+#define TYPIFY_j int*
+#define LOOPER_j for (int j_i = 0; j_i < 12; ++j_i) {
+#define VMAKER_j                                          \
+  int* j = ijarr + j_i;                                   \
+  string j_st = folly::to<string>("j = fib[", j_i, "]");  \
+  SCOPED_TRACE(j_st);
+#define UMAKER_j \
+  for (int j_c = 0; j_c < 12; ++j_c) ASSERT_EQ(ijarC[j_c], ijarr[j_c]);
+#define CLOSER_j }
+
+//-------------------
+// internal iterators
+
+template <class Vector>
+std::pair<typename Vector::iterator, string>
+iterSpotter(Vector& v, int i) {
+  typename Vector::iterator it;
+  string msg;
+
+  switch(i) {
+  case 1:
+    if (v.empty()) ; // fall through
+    else {
+      it = v.begin();
+      ++it;
+      msg = "a[1]";
+      break;
+    }
+  case 0:
+    it = v.begin();
+    msg = "a.begin";
+    break;
+
+  case 2:
+    if (v.empty()) ; // fall through
+    else {
+      it = v.end();
+      --it;
+      msg = "a[-1]";
+      break;
+    }
+  case 3:
+    it = v.end();
+    msg = "a.end";
+    break;
+
+  default:
+    cerr << "internal error" << endl;
+    exit(1);
+  }
+
+  return make_pair(it, msg);
+}
+
+#define TYPIFY_p typename Vector::iterator
+#define LOOPER_p for (int p_i = 0; p_i < 4; ++p_i) {
+#define VMAKER_p                    \
+  auto p_im = iterSpotter(a, p_i);  \
+  auto& p = p_im.first;             \
+  auto& p_m = p_im.second;          \
+  SCOPED_TRACE("p = " + p_m);
+#define UMAKER_p
+#define CLOSER_p }
+
+#define TYPIFY_q typename Vector::iterator
+#define LOOPER_q for (int q_i = p_i; q_i < 4; ++q_i) {
+#define VMAKER_q                    \
+  auto q_im = iterSpotter(a, q_i);  \
+  auto& q = q_im.first;             \
+  auto& q_m = q_im.second;          \
+  SCOPED_TRACE("q = " + q_m);
+#define UMAKER_q
+#define CLOSER_q }
+
+//---------
+// datatype
+
+static const vector<int> tVals = { 0, 1, 2, 3, 17, 66, 521 };
+
+#define TYPIFY_t typename Vector::value_type&
+#define LOOPER_t for (int t_v : tVals) {
+#define VMAKER_t                                                            \
+  typename Vector::value_type t_s(t_v);                                     \
+  t_p = addressof(t_s);                                                     \
+  string t_st = folly::to<string>("t(", t_v, ")");                          \
+  if (t_v < 4 && a_p != nullptr) {                                          \
+    auto t_im = iterSpotter(*a_p, t_v);                                     \
+    if (t_im.first != a_p->end()) {                                         \
+      t_p = addressof(*t_im.first);                                         \
+      t_st = "t is " + t_im.second;                                         \
+    }                                                                       \
+  }                                                                         \
+  typename Vector::value_type& t = *t_p;                                    \
+  SCOPED_TRACE(t_st);
+#define UMAKER_t
+#define CLOSER_t }
+
+//----------
+// allocator
+
+#define TYPIFY_m typename Vector::allocator_type
+#define LOOPER_m                          \
+  int m_max = 1 + (a_p != nullptr);       \
+  for (int m_i = 0; m_i < m_max; ++m_i) {
+#define VMAKER_m                                \
+  typename Vector::allocator_type m = m_i == 0  \
+    ? typename Vector::allocator_type()         \
+    : a_p->get_allocator();
+#define UMAKER_m
+#define CLOSER_m }
+
+//-----------------------------------------------------------------------------
+// Verifiers
+
+// verify a vector
+template <class Vector>
+void verifyVector(const Vector& v) {
+  ASSERT_TRUE(v.begin() <= v.end()) << "end is before begin";
+  ASSERT_TRUE(v.empty() == (v.begin() == v.end())) << "empty != (begin == end)";
+  ASSERT_TRUE(v.size() == distance(v.begin(), v.end()))
+    << "size != end - begin";
+  ASSERT_TRUE(v.size() <= v.capacity()) << "size > capacity";
+  ASSERT_TRUE(v.capacity() <= v.max_size()) << "capacity > max_size";
+  ASSERT_TRUE(v.data() || true); // message won't print - it will just crash
+  ASSERT_TRUE(v.size() == 0 || v.data() != nullptr)
+    << "nullptr data points to at least one element";
+}
+
+void verifyAllocator(int ele, int cap) {
+  ASSERT_EQ(ele, AllocTracker::Constructed - AllocTracker::Destroyed);
+
+  int tot = 0;
+  for (auto kv : AllocTracker::Allocated)
+    if (kv.second != -1) tot += kv.second;
+  ASSERT_EQ(cap, tot) << "the allocator counts " << tot << " space, "
+    "but the vector(s) have (combined) capacity " << cap;
+}
+
+// Master verifier
+template <class Vector>
+void verify(int extras) {
+  if (!is_arithmetic<typename Vector::value_type>::value)
+    ASSERT_EQ(0 + extras, getTotal()) << "there exist Data but no vectors";
+  isSane();
+  if (::testing::Test::HasFatalFailure()) return;
+  if (customAllocator<Vector>::value) verifyAllocator(0, 0);
+}
+template <class Vector>
+void verify(int extras, const Vector& v) {
+  verifyVector(v);
+  if (!is_arithmetic<typename Vector::value_type>::value)
+    ASSERT_EQ(v.size() + extras, getTotal())
+      << "not all Data are in the vector";
+  isSane();
+  if (::testing::Test::HasFatalFailure()) return;
+  if (customAllocator<Vector>::value) verifyAllocator(v.size(), v.capacity());
+}
+template <class Vector>
+void verify(int extras, const Vector& v1, const Vector& v2) {
+  verifyVector(v1);
+  verifyVector(v2);
+  auto size = v1.size();
+  auto cap = v1.capacity();
+  if (&v1 != &v2) {
+    size += v2.size();
+    cap += v2.capacity();
+  }
+  if (!is_arithmetic<typename Vector::value_type>::value)
+    ASSERT_EQ(size + extras, getTotal()) << "not all Data are in the vector(s)";
+  isSane();
+  if (::testing::Test::HasFatalFailure()) return;
+  if (customAllocator<Vector>::value) verifyAllocator(size, cap);
+}
+
+//=============================================================================
+// Helpers
+
+// save the state of a vector
+int convertToInt(int t) {
+  return t;
+}
+template <Flags f, size_t pad>
+int convertToInt(const Data<f, pad>& t) {
+  return t.uid;
+}
+template <typename T>
+int convertToInt(const std::allocator<T>&) {
+  return -1;
+}
+template <typename T>
+int convertToInt(const Alloc<T>& a) {
+  return a.id;
+}
+
+template <class Vector>
+class DataState {
+  typedef typename Vector::size_type size_type;
+  size_type size_;
+  int* data_;
+public:
+  /* implicit */ DataState(const Vector& v) {
+    size_ = v.size();
+    if (size_ != 0) {
+      data_ = new int[size_];
+      for (size_type i = 0; i < size_; ++i) {
+        data_[i] = convertToInt(v.data()[i]);
+      }
+    } else {
+      data_ = nullptr;
+    }
+  }
+  ~DataState() {
+    delete[] data_;
+  }
+
+  bool operator==(const DataState& o) const {
+    if (size_ != o.size_) return false;
+    for (size_type i = 0; i < size_; ++i) {
+      if (data_[i] != o.data_[i]) return false;
+    }
+    return true;
+  }
+
+  int operator[](size_type i) {
+    if (i >= size_) {
+      cerr << "trying to access DataState out of bounds" << endl;
+      exit(1);
+    }
+    return data_[i];
+  }
+
+  size_type size() { return size_; }
+};
+
+// downgrade iterators
+template <typename It, class tag>
+class Transformer : public boost::iterator_adaptor<
+                            Transformer<It, tag>,
+                            It,
+                            typename iterator_traits<It>::value_type,
+                            tag
+                           > {
+  friend class boost::iterator_core_access;
+  shared_ptr<set<It>> dereferenced;
+
+public:
+  explicit Transformer(const It& it)
+    : Transformer::iterator_adaptor_(it)
+    , dereferenced(new set<It>()) {}
+
+  typename iterator_traits<It>::value_type& dereference() const {
+    if (dereferenced->find(this->base_reference()) != dereferenced->end()) {
+      cerr << "iterator dereferenced more than once" << endl;
+      exit(1);
+    }
+    dereferenced->insert(this->base_reference());
+    return *this->base_reference();
+  }
+};
+
+template <typename It>
+Transformer<It, forward_iterator_tag> makeForwardIterator(const It& it) {
+  return Transformer<It, forward_iterator_tag>(it);
+}
+template <typename It>
+Transformer<It, input_iterator_tag> makeInputIterator(const It& it) {
+  return Transformer<It, input_iterator_tag>(it);
+}
+
+// mutate a value (in contract only)
+void mutate(int& i) {
+  if (false) i = 0;
+}
+void mutate(uint64_t& i) {
+  if (false) i = 0;
+}
+template <Flags f, size_t pad>
+void mutate(Data<f, pad>& ds) {
+  if (false) ds.uid = 0;
+}
+
+//=============================================================================
+// Tests
+
+// #if 0
+
+
+
+// #else
+
+//-----------------------------------------------------------------------------
+// Container
+
+STL_TEST("23.2.1 Table 96.1-7", containerTypedefs, is_destructible) {
+  static_assert(is_same<T, typename Vector::value_type>::value,
+    "T != Vector::value_type");
+  static_assert(is_same<T&, typename Vector::reference>::value,
+    "T& != Vector::reference");
+  static_assert(is_same<const T&, typename Vector::const_reference>::value,
+    "const T& != Vector::const_reference");
+  static_assert(is_convertible<
+      typename iterator_traits<typename Vector::iterator>::iterator_category,
+      forward_iterator_tag>::value,
+    "Vector::iterator is not a forward iterator");
+  static_assert(is_same<T,
+      typename iterator_traits<typename Vector::iterator>::value_type>::value,
+    "Vector::iterator does not iterate over type T");
+  static_assert(is_convertible<
+      typename iterator_traits<typename Vector::const_iterator>
+        ::iterator_category,
+      forward_iterator_tag>::value,
+    "Vector::const_iterator is not a forward iterator");
+  static_assert(is_same<T,
+      typename iterator_traits<typename Vector::const_iterator>
+        ::value_type>::value,
+    "Vector::const_iterator does not iterate over type T");
+  static_assert(is_convertible<
+      typename Vector::iterator, typename Vector::const_iterator>::value,
+    "Vector::iterator is not convertible to Vector::const_iterator");
+  static_assert(is_signed<typename Vector::difference_type>::value,
+    "Vector::difference_type is not signed");
+  static_assert(is_same<typename Vector::difference_type,
+        typename iterator_traits<typename Vector::iterator>
+      ::difference_type>::value,
+    "Vector::difference_type != Vector::iterator::difference_type");
+  static_assert(is_same<typename Vector::difference_type,
+        typename iterator_traits<typename Vector::const_iterator>
+      ::difference_type>::value,
+    "Vector::difference_type != Vector::const_iterator::difference_type");
+  static_assert(is_unsigned<typename Vector::size_type>::value,
+    "Vector::size_type is not unsigned");
+  static_assert(sizeof(typename Vector::size_type) >=
+      sizeof(typename Vector::difference_type),
+    "Vector::size_type is smaller than Vector::difference_type");
+}
+
+STL_TEST("23.2.1 Table 96.8-9", emptyConstruction, is_destructible) {
+  Vector u;
+
+  ASSERT_TRUE(u.get_allocator() == Allocator());
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  ASSERT_TRUE(u.empty()) << u.size();
+  ASSERT_EQ(0, u.capacity());
+
+  if (false) {
+    Vector();
+  }
+}
+
+STL_TEST("framework", populate, is_copy_constructible) {
+  // We use emplace_back to construct vectors for testing, as well as size,
+  // data, and capacity. We make sure these work before proceeding with tests.
+
+  Vector u;
+  ASSERT_EQ(0, u.size());
+  ASSERT_EQ(nullptr, u.data());
+
+  u.emplace_back(17);
+  ASSERT_EQ(1, u.size());
+  ASSERT_LT(u.capacity(), 100)
+    << "single push_back increased capacity to " << u.capacity();
+  ASSERT_NE(nullptr, u.data());
+  ASSERT_EQ(17, convertToInt(u.data()[0]))
+    << "first object did not get emplaced correctly";
+
+  for (int i = 0; i < 3; ++i) {
+    auto cap = u.capacity();
+    while (u.size() < cap) {
+      u.emplace_back(22);
+      ASSERT_EQ(cap, u.capacity()) << "Vector grew when it did not need to";
+      ASSERT_EQ(22, convertToInt(u.data()[u.size() - 1]))
+        << "push_back with excess capacity failed";
+    }
+
+    ASSERT_EQ(cap, u.size());
+
+    u.emplace_back(4);
+    ASSERT_GT(u.capacity(), cap) << "capacity did not grow on overflow";
+    ASSERT_EQ(cap + 1, u.size());
+    ASSERT_EQ(4, convertToInt(u.data()[u.size() - 1]))
+      << "grow object did not get emplaced correctly";
+  }
+}
+
+STL_TEST("23.2.1 Table 96.10-11", copyConstruction,
+          is_copy_constructible, a) {
+  const auto& ca = a;
+  DataState<Vector> dsa(ca);
+  auto am = a.get_allocator();
+
+  Vector u(ca);
+
+  ASSERT_TRUE(std::allocator_traits<Allocator>::
+    select_on_container_copy_construction(am) == u.get_allocator());
+  ASSERT_TRUE(dsa == u);
+  ASSERT_TRUE(
+    (ca.data() == nullptr && u.data() == nullptr) ||
+    (ca.data() != u.data())
+  ) << "only a shallow copy was made";
+
+  if (false) {
+    Vector(ca);
+    Vector u = ca;
+  }
+}
+
+STL_TEST("23.2.1 Table 96.12", moveConstruction, is_destructible, a) {
+  DataState<Vector> dsa(a);
+  auto m = a.get_allocator();
+
+  Vector u(move(a));
+
+  ASSERT_TRUE(m == u.get_allocator());
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  ASSERT_TRUE(dsa == u);
+
+  if (false) {
+    Vector u = move(a);
+  }
+}
+
+STL_TEST("23.2.1 Table 96.13", moveAssignment, special_move_assignable, a, b) {
+  DataState<Vector> dsb(b);
+  auto am = a.get_allocator();
+  auto bm = b.get_allocator();
+
+  Vector& ret = a = std::move(b);
+
+  if (std::allocator_traits<Allocator>::
+      propagate_on_container_move_assignment::value) {
+    ASSERT_TRUE(bm == a.get_allocator());
+  } else {
+    ASSERT_TRUE(am == a.get_allocator());
+  }
+  ASSERT_TRUE(&ret == &a);
+  ASSERT_TRUE(&a == &b || dsb == a) << "move assignment did not create a copy";
+  // The source of the move may be left in any (albeit valid) state.
+}
+
+STL_TEST("23.2.1 Table 96.14", destructible, is_destructible) {
+  // The test generators check this clause already.
+}
+
+STL_TEST("23.2.1 Table 96.15-18", iterators, is_destructible, a) {
+  DataState<Vector> dsa(a);
+  const auto& ca = a;
+
+  auto  itb =  a.begin();
+  auto citb = ca.begin();
+  auto Citb =  a.cbegin();
+  auto  ite =  a.end();
+  auto cite = ca.end();
+  auto Cite =  a.cend();
+
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  ASSERT_TRUE(dsa == a) << "call to begin or end modified internal data";
+
+  ASSERT_TRUE(citb == Citb) << "cv.begin != v.cbegin";
+  ASSERT_TRUE(cite == Cite) << "cv.end != v.cend";
+
+  if (ca.size() == 0) {
+    ASSERT_TRUE( itb ==  ite) << "begin != end when empty";
+    ASSERT_TRUE(Citb == Cite) << "cbegin != cend when empty";
+  } else {
+    ASSERT_TRUE( itb !=  ite) << "begin == end when non-empty";
+    ASSERT_TRUE(Citb != Cite) << "cbegin == cend when non-empty";
+  }
+
+  auto  dist = std::distance( itb,  ite);
+  auto Cdist = std::distance(Citb, Cite);
+  ASSERT_TRUE( dist == ca.size()) << "distance(begin, end) != size";
+  ASSERT_TRUE(Cdist == ca.size()) << "distance(cbegin, cend) != size";
+}
+
+STL_TEST("23.2.1 Table 96.19-20", equitable, is_arithmetic, a, b) {
+  const auto& ca = a;
+  const auto& cb = b;
+  DataState<Vector> dsa(a);
+  DataState<Vector> dsb(b);
+
+  ASSERT_TRUE((bool)(ca == cb) == (bool)(dsa == dsb))
+    << "== does not return equality";
+  ASSERT_TRUE((bool)(ca == cb) != (bool)(ca != cb))
+    << "!= is not the opposite of ==";
+
+  // Data is uncomparable, by design; therefore this test's restriction
+  // is 'is_arithmetic'
+}
+
+STL_TEST("23.2.1 Table 96.21", memberSwappable, is_destructible, a, b) {
+  if (!std::allocator_traits<Allocator>::
+        propagate_on_container_swap::value &&
+      convertToInt(a.get_allocator()) != convertToInt(b.get_allocator())) {
+    // undefined behaviour
+    return;
+  }
+
+  DataState<Vector> dsa(a);
+  DataState<Vector> dsb(b);
+  auto adata = a.data();
+  auto bdata = b.data();
+  auto am = a.get_allocator();
+  auto bm = b.get_allocator();
+
+  try {
+    a.swap(b);
+  } catch (...) {
+    FAIL() << "swap is noexcept";
+  }
+
+  if (std::allocator_traits<Allocator>::
+      propagate_on_container_swap::value) {
+    ASSERT_TRUE(bm == a.get_allocator());
+    ASSERT_TRUE(am == b.get_allocator());
+  } else {
+    ASSERT_TRUE(am == a.get_allocator());
+    ASSERT_TRUE(bm == b.get_allocator());
+  }
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  ASSERT_TRUE(adata == b.data() && bdata == a.data());
+  ASSERT_TRUE(dsa == b && dsb == a) << "swap did not swap";
+}
+
+STL_TEST("23.2.1 Table 96.22", nonmemberSwappable,
+         is_destructible, a, b) {
+  if (!std::allocator_traits<Allocator>::
+        propagate_on_container_swap::value &&
+      convertToInt(a.get_allocator()) != convertToInt(b.get_allocator())) {
+    // undefined behaviour
+    return;
+  }
+
+  DataState<Vector> dsa(a);
+  DataState<Vector> dsb(b);
+  auto adata = a.data();
+  auto bdata = b.data();
+  auto am = a.get_allocator();
+  auto bm = b.get_allocator();
+
+  try {
+    swap(a, b);
+  } catch (...) {
+    FAIL() << "swap is noexcept";
+  }
+
+  if (std::allocator_traits<Allocator>::
+      propagate_on_container_swap::value) {
+    ASSERT_TRUE(bm == a.get_allocator());
+    ASSERT_TRUE(am == b.get_allocator());
+  } else {
+    ASSERT_TRUE(am == a.get_allocator());
+    ASSERT_TRUE(bm == b.get_allocator());
+  }
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  ASSERT_TRUE(adata == b.data() && bdata == a.data());
+  ASSERT_TRUE(dsa == b && dsb == a) << "swap did not swap";
+}
+
+STL_TEST("23.2.1 Table 96.23", copyAssign,
+          is_copy_constructibleAndAssignable, a, b) {
+  // it is possible to make use of just the copy constructor.
+
+  #ifdef USING_STD_VECTOR
+  if (std::allocator_traits<Allocator>::
+        propagate_on_container_copy_assignment::value &&
+      convertToInt(a.get_allocator()) != convertToInt(b.get_allocator())) {
+    // Bug. By the looks of things, in the above case, their bez is being
+    // cleared and deallocated, but then the garbage pointers are being used.
+    return;
+  }
+  #endif
+
+  const auto& cb = b;
+  DataState<Vector> dsb(cb);
+  auto am = a.get_allocator();
+  auto bm = b.get_allocator();
+
+  Vector& ret = a = cb;
+
+  if (std::allocator_traits<Allocator>::
+      propagate_on_container_copy_assignment::value) {
+    ASSERT_TRUE(bm == a.get_allocator());
+  } else {
+    ASSERT_TRUE(am == a.get_allocator());
+  }
+  ASSERT_TRUE(&ret == &a);
+  ASSERT_TRUE(dsb == a) << "copy-assign not equal to original";
+}
+
+STL_TEST("23.2.1 Table 96.24-26", sizeops, is_destructible) {
+  // This check generators check this clause already.
+}
+
+//-----------------------------------------------------------------------------
+// Reversible container
+
+STL_TEST("23.2.1 Table 97.1-2", reversibleContainerTypedefs,
+          is_destructible) {
+  static_assert(is_same<typename Vector::reverse_iterator,
+      std::reverse_iterator<typename Vector::iterator>>::value,
+    "Vector::reverse_iterator != reverse_iterator<Vector:iterator");
+  static_assert(is_same<typename Vector::const_reverse_iterator,
+      std::reverse_iterator<typename Vector::const_iterator>>::value,
+    "Vector::const_reverse_iterator != "
+    "const_reverse_iterator<Vector::iterator");
+}
+
+STL_TEST("23.2.1 Table 97.3-5", reversibleIterators, is_destructible, a) {
+  const auto& ca = a;
+  DataState<Vector> ds(a);
+
+  auto  ritb =  a.rbegin();
+  auto critb = ca.rbegin();
+  auto Critb =  a.crbegin();
+  auto  rite =  a.rend();
+  auto crite = ca.rend();
+  auto Crite =  a.crend();
+
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  ASSERT_TRUE(ds == a) << "call to rbegin or rend modified internal data";
+
+  ASSERT_TRUE(critb == Critb) << "cv.rbegin != v.crbegin";
+  ASSERT_TRUE(crite == Crite) << "cv.rend != v.crend";
+
+  if (ca.size() == 0) {
+    ASSERT_TRUE( ritb ==  rite) << "rbegin != rend when empty";
+    ASSERT_TRUE(Critb == Crite) << "crbegin != crend when empty";
+  } else {
+    ASSERT_TRUE( ritb !=  rite) << "rbegin == rend when non-empty";
+    ASSERT_TRUE(Critb != Crite) << "crbegin == crend when non-empty";
+  }
+
+  auto  dist = std::distance( ritb,  rite);
+  auto Cdist = std::distance(Critb, Crite);
+  ASSERT_TRUE( dist == ca.size()) << "distance(rbegin, rend) != size";
+  ASSERT_TRUE(Cdist == ca.size()) << "distance(crbegin, crend) != size";
+}
+
+//-----------------------------------------------------------------------------
+// Lexicographical functions
+
+STL_TEST("23.2.1 Table 98", comparable, is_arithmetic) {
+  const Vector v1 = { 1, 2, 3, 4 };
+  const Vector v2 = { 1, 2, 3, 4, 5 };
+  const Vector v3 = { 1, 2, 2 };
+  const Vector v4 = { 1, 2, 2, 4, 5 };
+  const Vector v5 = { };
+  const Vector v6 = { 1, 2, 3, 4 };
+
+  ASSERT_TRUE(v1 < v2);
+  ASSERT_TRUE(v1 > v3);
+  ASSERT_TRUE(v1 > v4);
+  ASSERT_TRUE(v1 > v5);
+  ASSERT_TRUE(v1 <= v6);
+  ASSERT_TRUE(v1 >= v6);
+}
+
+//-----------------------------------------------------------------------------
+// Allocator-aware requirements (AA)
+
+STL_TEST("23.2.1 Table 99.1", allocatorTypedefs, is_destructible) {
+  static_assert(is_same<T, typename Vector::allocator_type::value_type>::value,
+    "Vector and vector's allocator value_type mismatch");
+}
+
+STL_TEST("23.2.1 Table 99.2", getAllocator, is_destructible) {
+  // whitebox: ensure that a.get_allocator() returns a copy of its allocator
+}
+
+STL_TEST("23.2.1 Table 99.3", defaultAllocator, is_destructible) {
+  // there is nothing new to test here
+}
+
+STL_TEST("23.2.1 Table 99.4", customAllocator, is_destructible, m) {
+  const auto& cm = m;
+
+  Vector u(cm);
+
+  ASSERT_TRUE(u.get_allocator() == m);
+
+  if (false) {
+    Vector(m);
+  }
+}
+
+STL_TEST("23.2.1 Table 99.5", copyWithAllocator, is_copy_constructible, a, m) {
+  DataState<Vector> dsa(a);
+  const auto& ca = a;
+  const auto& cm = m;
+
+  Vector u(ca, cm);
+
+  ASSERT_TRUE(u.get_allocator() == m);
+  ASSERT_TRUE(dsa == u);
+  ASSERT_TRUE(
+    (ca.data() == nullptr && u.data() == nullptr) ||
+    (ca.data() != u.data())
+  ) << "only a shallow copy was made";
+}
+
+STL_TEST("23.2.1 Table 99.6", moveConstructionWithAllocator,
+         is_destructible, a) {
+  // there is nothing new to test here
+}
+
+STL_TEST("23.2.1 Table 99.6", moveConstructionWithAllocatorSupplied,
+         is_move_constructible, a, m) {
+  bool deep = m != a.get_allocator();
+  auto osize = a.size();
+  auto oalloc = AllocTracker::Constructed;
+  const auto& cm = m;
+
+  Vector u(std::move(a), cm);
+
+  ASSERT_TRUE(u.get_allocator() == m);
+
+  if (deep) {
+    if (!AllocTracker::Allocated.empty()) {
+      ASSERT_EQ(osize, AllocTracker::Constructed - oalloc);
+    }
+  } else {
+    ASSERT_EQ(0, Counter::CountTotalOps);
+  }
+}
+
+STL_TEST("23.2.1 Table 99.7-9", allocAssign, is_destructible) {
+  // there is nothing new to test here
+}
+
+STL_TEST("23.2.1-7", nAllocConstruction, is_copy_constructible, n, m) {
+  #ifndef USING_STD_VECTOR
+  const auto& cm = m;
+
+  Vector u(n, cm);
+
+  ASSERT_TRUE(m == u.get_allocator());
+  #endif
+}
+
+STL_TEST("23.2.1-7", nCopyAllocConstruction, is_copy_constructible, n, t, m) {
+  const auto& cm = m;
+  const auto& ct = t;
+
+  Vector u(n, ct, cm);
+
+  ASSERT_TRUE(m == u.get_allocator());
+}
+
+STL_TEST("23.2.1-7", forwardIteratorAllocConstruction,
+         is_destructible, i, j, m) {
+  auto fi = makeForwardIterator(i);
+  auto fj = makeForwardIterator(j);
+  const auto& cfi = fi;
+  const auto& cfj = fj;
+  const auto& cm = m;
+
+  Vector u(cfi, cfj, cm);
+
+  ASSERT_TRUE(m == u.get_allocator());
+}
+
+STL_TEST("23.2.1-7", inputIteratorAllocConstruction,
+         is_move_constructible, i, j, m) {
+  #ifdef USING_STD_VECTOR
+  if (Ticker::TicksLeft >= 0) return;
+  #endif
+
+  auto ii = makeInputIterator(i);
+  auto ij = makeInputIterator(j);
+  const auto& cii = ii;
+  const auto& cij = ij;
+  const auto& cm = m;
+
+  Vector u(cii, cij, cm);
+
+  ASSERT_TRUE(m == u.get_allocator());
+}
+
+STL_TEST("23.2.1-7", ilAllocConstruction, is_arithmetic, m) {
+  // gcc fail
+  if (Ticker::TicksLeft >= 0) return;
+
+  const auto& cm = m;
+
+  Vector u({ 1, 4, 7 }, cm);
+
+  ASSERT_TRUE(m == u.get_allocator());
+}
+
+//-----------------------------------------------------------------------------
+// Data races
+
+STL_TEST("23.2.2", dataRaces, is_destructible) {
+  if (false) {
+    const Vector* cv = nullptr;
+    typename Vector::size_type* s = nullptr;
+
+    cv->begin();
+    cv->end();
+    cv->rbegin();
+    cv->rend();
+    cv->front();
+    cv->back();
+    cv->data();
+
+    (*cv).at(*s);
+    (*cv)[*s];
+  }
+
+  // White-box: check that the non-const versions of each of the above
+  // functions is implemented in terms of (or the same as) the const version
+}
+
+//-----------------------------------------------------------------------------
+// Sequence container
+
+STL_TEST("23.2.3 Table 100.1, alt", nConstruction, is_constructible, n) {
+  Vector u(n);
+
+  ASSERT_TRUE(Allocator() == u.get_allocator());
+  ASSERT_EQ(n, u.size());
+  ASSERT_EQ(Counter::CountTotalOps, Counter::CountDC);
+}
+
+STL_TEST("23.2.3 Table 100.1", nCopyConstruction,
+         is_copy_constructible, n, t) {
+  const auto& ct = t;
+
+  Vector u(n, ct);
+
+  ASSERT_TRUE(Allocator() == u.get_allocator());
+  ASSERT_EQ(n, u.size()) << "Vector(n, t).size() != n" << endl;
+  for (const auto& val : u) ASSERT_EQ(convertToInt(t), convertToInt(val))
+    << "not all elements of Vector(n, t) are equal to t";
+}
+
+STL_TEST("23.2.3 Table 100.2", forwardIteratorConstruction,
+         is_destructible, i, j) {
+  // All data is emplace-constructible from int, so we restrict to
+  // is_destructible
+
+  auto fi = makeForwardIterator(i);
+  auto fj = makeForwardIterator(j);
+  const auto& cfi = fi;
+  const auto& cfj = fj;
+
+  Vector u(cfi, cfj);
+
+  ASSERT_TRUE(Allocator() == u.get_allocator());
+  ASSERT_LE(Counter::CountTotalOps, j-i);
+
+  ASSERT_EQ(j - i, u.size()) << "u(i,j).size() != j-i";
+  for (auto it = u.begin(); it != u.end(); ++it, ++i)
+    ASSERT_EQ(*i, convertToInt(*it)) << "u(i,j) constructed incorrectly";
+}
+
+STL_TEST("23.2.3 Table 100.2", inputIteratorConstruction,
+         is_move_constructible, i, j) {
+  #ifdef USING_STD_VECTOR
+  if (Ticker::TicksLeft >= 0) return;
+  #endif
+
+  auto ii = makeInputIterator(i);
+  auto ij = makeInputIterator(j);
+  const auto& cii = ii;
+  const auto& cij = ij;
+
+  Vector u(cii, cij);
+
+  ASSERT_TRUE(Allocator() == u.get_allocator());
+  ASSERT_EQ(j - i, u.size()) << "u(i,j).size() != j-i";
+  for (auto it = u.begin(); it != u.end(); ++it, ++i)
+    ASSERT_EQ(*i, convertToInt(*it)) << "u(i,j) constructed incorrectly";
+}
+
+STL_TEST("23.2.3 Table 100.3", ilConstruction, is_arithmetic) {
+  // whitebox: ensure that Vector(il) is implemented in terms of
+  // Vector(il.begin(), il.end())
+
+  // gcc fail
+  if (Ticker::TicksLeft >= 0) return;
+
+  Vector u = { 1, 4, 7 };
+
+  ASSERT_TRUE(Allocator() == u.get_allocator());
+  ASSERT_EQ(3, u.size()) << "u(il).size() fail";
+  int i = 1;
+  auto it = u.begin();
+  for (; it != u.end(); ++it, i += 3)
+    ASSERT_EQ(i, convertToInt(*it)) << "u(il) constructed incorrectly";
+}
+
+STL_TEST("23.2.3 Table 100.4", ilAssignment,
+         is_arithmetic, a) {
+  // whitebox: ensure that assign(il) is implemented in terms of
+  // assign(il.begin(), il.end())
+
+  // gcc fail
+  if (Ticker::TicksLeft >= 0) return;
+
+  auto am = a.get_allocator();
+
+  Vector& b = a = { 1, 4, 7 };
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_TRUE(&b == &a) << "'a = ...' did not return *this";
+
+  ASSERT_EQ(3, a.size()) << "u(il).size() fail";
+  int i = 1;
+  auto it = a.begin();
+  for (; it != a.end(); ++it, i += 3)
+    ASSERT_EQ(i, convertToInt(*it)) << "u(il) constructed incorrectly";
+}
+
+//----------------------------
+// insert-and-erase subsection
+
+template <class Vector>
+void insertNTCheck(const Vector& a, DataState<Vector>& dsa,
+                   int idx, int n, int val) {
+  ASSERT_EQ(dsa.size() + n, a.size());
+  int i = 0;
+  for (; i < idx; ++i) {
+    ASSERT_EQ(dsa[i], convertToInt(a.data()[i])) << i;
+  }
+  for (; i < idx + n; ++i) {
+    ASSERT_EQ(val, convertToInt(a.data()[i])) << i;
+  }
+  for (; i < a.size(); ++i) {
+    ASSERT_EQ(dsa[i-n], convertToInt(a.data()[i])) << i;
+  }
+}
+
+STL_TEST("23.2.3 Table 100.5", iteratorEmplacement,
+         is_move_constructibleAndAssignable, a, p) {
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  auto am = a.get_allocator();
+
+  auto q = a.emplace(p, 44);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  insertNTCheck(a, dsa, idx, 1, 44);
+}
+
+STL_TEST("23.2.3 Table 100.6", iteratorInsertion,
+         is_copy_constructibleAndAssignable, a, p, t) {
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  int tval = convertToInt(t);
+  auto am = a.get_allocator();
+  const auto& ct = t;
+
+  auto q = a.insert(p, ct);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  insertNTCheck(a, dsa, idx, 1, tval);
+}
+
+STL_TEST("23.2.3 Table 100.7", iteratorInsertionRV,
+         is_move_constructibleAndAssignable, a, p, t) {
+  // rvalue-references cannot have their address checked for aliased inserts
+  if (a.data() <= addressof(t) && addressof(t) < a.data() + a.size()) return;
+
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  int tval = convertToInt(t);
+  auto am = a.get_allocator();
+
+  auto q = a.insert(p, std::move(t));
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  insertNTCheck(a, dsa, idx, 1, tval);
+}
+
+STL_TEST("23.2.3 Table 100.8", iteratorInsertionN,
+         is_copy_constructibleAndAssignable, a, p, n, t) {
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  int tval = convertToInt(t);
+  auto am = a.get_allocator();
+  const auto& ct = t;
+
+  #ifndef USING_STD_VECTOR
+  auto q =
+  #endif
+
+  a.insert(p, n, ct);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  #ifndef USING_STD_VECTOR
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  #endif
+
+  insertNTCheck(a, dsa, idx, n, tval);
+}
+
+template <class Vector>
+void insertItCheck(const Vector& a, DataState<Vector>& dsa,
+                   int idx, int* b, int* e) {
+  ASSERT_EQ(dsa.size() + (e - b), a.size());
+  int i = 0;
+  for (; i < idx; ++i) {
+    ASSERT_EQ(dsa[i], convertToInt(a.data()[i]));
+  }
+  for (; i < idx + (e - b); ++i) {
+    ASSERT_EQ(*(b + i - idx), convertToInt(a.data()[i]));
+  }
+  for (; i < a.size(); ++i) {
+    ASSERT_EQ(dsa[i - (e - b)], convertToInt(a.data()[i]));
+  }
+}
+
+STL_TEST("23.2.3 Table 100.9", iteratorInsertionIterator,
+         is_move_constructibleAndAssignable, a, p, i, j) {
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+
+  auto fi = makeForwardIterator(i);
+  auto fj = makeForwardIterator(j);
+  auto am = a.get_allocator();
+  const auto& cfi = fi;
+  const auto& cfj = fj;
+
+  #ifndef USING_STD_VECTOR
+  auto q =
+  #endif
+
+  a.insert(p, cfi, cfj);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  #ifndef USING_STD_VECTOR
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  #endif
+
+  insertItCheck(a, dsa, idx, i, j);
+}
+
+STL_TEST("23.2.3 Table 100.9", iteratorInsertionInputIterator,
+         is_move_constructibleAndAssignable, a, p, i, j) {
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+
+  auto ii = makeInputIterator(i);
+  auto ij = makeInputIterator(j);
+  auto am = a.get_allocator();
+  const auto& cii = ii;
+  const auto& cij = ij;
+
+  #ifndef USING_STD_VECTOR
+  auto q =
+  #endif
+
+  a.insert(p, cii, cij);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  #ifndef USING_STD_VECTOR
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  #endif
+
+  insertItCheck(a, dsa, idx, i, j);
+}
+
+STL_TEST("23.2.3 Table 100.10", iteratorInsertIL,
+         is_arithmetic, a, p) {
+  // gcc fail
+  if (Ticker::TicksLeft >= 0) return;
+
+  // whitebox: ensure that insert(p, il) is implemented in terms of
+  // insert(p, il.begin(), il.end())
+
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  auto am = a.get_allocator();
+
+  #ifndef USING_STD_VECTOR
+  auto q =
+  #endif
+
+  a.insert(p, {1, 4, 7});
+
+  ASSERT_TRUE(am == a.get_allocator());
+  #ifndef USING_STD_VECTOR
+  ASSERT_EQ(idx, distance(a.begin(), q)) << "incorrect iterator returned";
+  #endif
+
+  int ila[] = { 1, 4, 7 };
+  int* i = ila;
+  int* j = ila + 3;
+  insertItCheck(a, dsa, idx, i, j);
+}
+
+template <class Vector>
+void eraseCheck(Vector& a, DataState<Vector>& dsa, int idx, int n) {
+  ASSERT_EQ(dsa.size() - n, a.size());
+  size_t i = 0;
+  auto it = a.begin();
+  for (; it != a.end(); ++it, ++i) {
+    if (i == idx) i += n;
+    ASSERT_EQ(dsa[i], convertToInt(*it));
+  }
+}
+
+STL_TEST("23.2.3 Table 100.11", iteratorErase, is_move_assignable, a, p) {
+  if (p == a.end()) return;
+
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  auto am = a.get_allocator();
+
+  auto rit = a.erase(p);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(idx, distance(a.begin(), rit)) << "wrong iterator returned";
+  eraseCheck(a, dsa, idx, 1);
+}
+
+STL_TEST("23.2.3 Table 100.12", iteratorEraseRange,
+         is_move_assignable, a, p, q) {
+  if (p == a.end()) return;
+
+  DataState<Vector> dsa(a);
+  int idx = distance(a.begin(), p);
+  auto am = a.get_allocator();
+
+  auto rit = a.erase(p, q);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(idx, distance(a.begin(), rit)) << "wrong iterator returned";
+  eraseCheck(a, dsa, idx, distance(p,q));
+}
+
+//--------------------------------
+// end insert-and-erase subsection
+
+STL_TEST("23.2.3 Table 100.13", clear, is_destructible, a) {
+
+  auto am = a.get_allocator();
+
+  try {
+    a.clear();
+  } catch (...) {
+    FAIL() << "clear must be noexcept";
+  }
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_TRUE(a.empty());
+}
+
+STL_TEST("23.2.3 Table 100.14", assignRange, is_move_assignable, a, i, j) {
+  auto fi = makeForwardIterator(i);
+  auto fj = makeForwardIterator(j);
+  const auto& cfi = fi;
+  const auto& cfj = fj;
+  auto am = a.get_allocator();
+
+  a.assign(cfi, cfj);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(distance(i, j), a.size());
+  for (auto it = a.begin(); it != a.end(); ++it, ++i)
+    ASSERT_EQ(*i, convertToInt(*it));
+}
+
+STL_TEST("23.2.3 Table 100.14", assignInputRange,
+         is_move_constructibleAndAssignable, a, i, j) {
+  auto ii = makeInputIterator(i);
+  auto ij = makeInputIterator(j);
+  const auto& cii = ii;
+  const auto& cij = ij;
+  auto am = a.get_allocator();
+
+  a.assign(cii, cij);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(distance(i, j), a.size());
+  for (auto it = a.begin(); it != a.end(); ++it, ++i)
+    ASSERT_EQ(*i, convertToInt(*it));
+}
+
+STL_TEST("23.2.3 Table 100.15", assignIL,
+         is_arithmetic, a) {
+
+  // whitebox: ensure that assign(il) is implemented in terms of
+  // assign(il.begin(), il.end())
+
+  // gcc fail
+  if (Ticker::TicksLeft >= 0) return;
+
+  auto am = a.get_allocator();
+
+  a.assign({1, 4, 7});
+
+  ASSERT_TRUE(am == a.get_allocator());
+  int ila[] = { 1, 4, 7 };
+  int* i = ila;
+
+  ASSERT_EQ(3, a.size());
+  for (auto it = a.begin(); it != a.end(); ++it, ++i)
+    ASSERT_EQ(*i, convertToInt(*it));
+}
+
+STL_TEST("23.2.3 Table 100.16", assignN,
+         is_copy_constructibleAndAssignable, a, n, t) {
+  auto am = a.get_allocator();
+  auto const& ct = t;
+  auto tval = convertToInt(t);
+
+  a.assign(n, ct);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(n, a.size());
+  for (auto it = a.begin(); it != a.end(); ++it)
+    ASSERT_EQ(tval, convertToInt(*it));
+}
+
+STL_TEST("23.2.3 Table 101.1", front, is_destructible, a) {
+  if (a.empty()) return;
+
+  ASSERT_TRUE(addressof(a.front()) == a.data());
+
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  if (false) {
+    mutate(a.front());
+    const Vector& ca = a;
+    ca.front();
+  }
+}
+
+STL_TEST("23.2.3 Table 101.2", back, is_destructible, a) {
+  if (a.empty()) return;
+
+  ASSERT_TRUE(addressof(a.back()) == a.data() + a.size() - 1);
+
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  if (false) {
+    mutate(a.back());
+    const Vector& ca = a;
+    ca.back();
+  }
+}
+
+STL_TEST("23.2.3 Table 101.4", emplaceBack,
+         is_move_constructible, a) {
+  DataState<Vector> dsa(a);
+  auto adata = a.data();
+  int excess = a.capacity() - a.size();
+  auto am = a.get_allocator();
+
+  try {
+    a.emplace_back(44);
+  } catch (...) {
+    ASSERT_TRUE(dsa == a) << "failed strong exception guarantee";
+    throw;
+  }
+
+  ASSERT_TRUE(am == a.get_allocator());
+  if (excess > 0) ASSERT_TRUE(a.data() == adata) << "unnecessary relocation";
+  ASSERT_EQ(dsa.size() + 1, a.size());
+  size_t i = 0;
+  auto it = a.begin();
+  for (; i < dsa.size(); ++i, ++it)
+    ASSERT_EQ(dsa[i], convertToInt(*it));
+  ASSERT_EQ(44, convertToInt(a.back()));
+}
+
+STL_TEST("23.2.3 Table 101.7", pushBack, is_copy_constructible, a, t) {
+  DataState<Vector> dsa(a);
+  int tval = convertToInt(t);
+  auto adata = a.data();
+  int excess = a.capacity() - a.size();
+  auto am = a.get_allocator();
+  const auto& ct = t;
+
+  try {
+    a.push_back(ct);
+  } catch (...) {
+    ASSERT_TRUE(dsa == a) << "failed strong exception guarantee";
+    throw;
+  }
+
+  ASSERT_TRUE(am == a.get_allocator());
+  if (excess > 0) ASSERT_TRUE(a.data() == adata) << "unnecessary relocation";
+  ASSERT_EQ(dsa.size() + 1, a.size());
+  size_t i = 0;
+  auto it = a.begin();
+  for (; i < dsa.size(); ++i, ++it)
+    ASSERT_EQ(dsa[i], convertToInt(*it));
+  ASSERT_EQ(tval, convertToInt(a.back()));
+}
+
+STL_TEST("23.2.3 Table 101.8", pushBackRV,
+         is_move_constructible, a, t) {
+  DataState<Vector> dsa(a);
+  int tval = convertToInt(t);
+  auto adata = a.data();
+  int excess = a.capacity() - a.size();
+  auto am = a.get_allocator();
+
+  try {
+    a.push_back(move(t));
+  } catch (...) {
+    ASSERT_TRUE(dsa == a) << "failed strong exception guarantee";
+    throw;
+  }
+
+  ASSERT_TRUE(am == a.get_allocator());
+  if (excess > 0) ASSERT_TRUE(a.data() == adata) << "unnecessary relocation";
+  ASSERT_EQ(dsa.size() + 1, a.size());
+  size_t i = 0;
+  auto it = a.begin();
+  for (; i < dsa.size(); ++i, ++it)
+    ASSERT_EQ(dsa[i], convertToInt(*it));
+  ASSERT_EQ(tval, convertToInt(a.back()));
+}
+
+STL_TEST("23.2.3 Table 100.10", popBack, is_destructible, a) {
+  if (a.empty()) return;
+
+  DataState<Vector> dsa(a);
+  auto am = a.get_allocator();
+
+  a.pop_back();
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(dsa.size() - 1, a.size());
+  size_t i = 0;
+  auto it = a.begin();
+  for (; it != a.end(); ++it, ++i)
+    ASSERT_EQ(dsa[i], convertToInt(*it));
+}
+
+STL_TEST("23.2.3 Table 100.11", operatorBrace, is_destructible, a) {
+  const auto& ca = a;
+  for (int i = 0; i < ca.size(); ++i)
+    ASSERT_TRUE(addressof(ca[i]) == ca.data()+i);
+
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  if (false) {
+    mutate(a[0]);
+  }
+}
+
+STL_TEST("23.2.3 Table 100.12", at, is_destructible, a) {
+  const auto& ca = a;
+  for (int i = 0; i < ca.size(); ++i)
+    ASSERT_TRUE(addressof(ca.at(i)) == ca.data()+i);
+
+  ASSERT_EQ(0, Counter::CountTotalOps);
+
+  try {
+    ca.at(ca.size());
+    FAIL() << "at(size) should have thrown an error";
+  } catch (const std::out_of_range& e) {
+  } catch (...) {
+    FAIL() << "at(size) threw error other than out_of_range";
+  }
+
+  if (false) {
+    mutate(a.at(0));
+  }
+}
+
+STL_TEST("move iterators", moveIterators, is_copy_constructibleAndAssignable) {
+  if (false) {
+    int* i = nullptr;
+    int* j = nullptr;
+
+    auto mfi = make_move_iterator(makeForwardIterator(i));
+    auto mfj = make_move_iterator(makeForwardIterator(j));
+    auto mii = make_move_iterator(makeInputIterator(i));
+    auto mij = make_move_iterator(makeInputIterator(j));
+
+    Vector u1(mfi, mfj);
+    Vector u2(mii, mij);
+
+    u1.insert(u1.begin(), mfi, mfj);
+    u1.insert(u1.begin(), mii, mij);
+
+    u1.assign(mfi, mfj);
+    u1.assign(mii, mij);
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Vector-specifics
+
+STL_TEST("23.3.6.4", dataAndCapacity, is_destructible) {
+  // there isn't anything new to test here - data and capacity are used as the
+  // backbone of DataState. The minimal testing we might want to do is already
+  // done in the populate test
+}
+
+STL_TEST("23.3.6.3", reserve, is_move_constructible, a, n) {
+  auto adata = a.data();
+  auto ocap = a.capacity();
+  auto am = a.get_allocator();
+
+  a.reserve(n);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  if (n <= ocap) {
+    ASSERT_EQ(0, Counter::CountTotalOps);
+    ASSERT_TRUE(adata == a.data());
+  } else {
+    ASSERT_TRUE(a.capacity() >= n);
+    ASSERT_LE(Counter::CountTotalOps, 2*a.size()); // move and delete
+  }
+}
+
+STL_TEST("23.3.6.3", lengthError, is_move_constructible) {
+  auto mx = Vector().max_size();
+  auto big = mx+1;
+  if (mx >= big) return; // max_size is the biggest size_type; overflowed
+
+  Vector u;
+  try {
+    u.reserve(big);
+    FAIL() << "reserve(big) should have thrown an error";
+  } catch (const std::length_error& e) {
+  } catch (...) {
+    FAIL() << "reserve(big) threw error other than length_error";
+  }
+}
+
+STL_TEST("23.3.6.3", resize, is_copy_constructible, a, n) {
+  DataState<Vector> dsa(a);
+  int sz = a.size();
+  auto am = a.get_allocator();
+
+  a.resize(n);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(n, a.size());
+
+  if (n <= sz) {
+    for (int i = 0; i < n; ++i) {
+      ASSERT_EQ(dsa[i], convertToInt(a[i]));
+    }
+  } else {
+    for (int i = 0; i < sz; ++i) {
+      ASSERT_EQ(dsa[i], convertToInt(a[i]));
+    }
+  }
+}
+
+STL_TEST("23.3.6.3", resizeT, is_copy_constructibleAndAssignable, a, n, t) {
+  #ifdef USING_STD_VECTOR
+  if (a.data() <= addressof(t) && addressof(t) < a.data() + a.size()) return;
+  #endif
+
+  DataState<Vector> dsa(a);
+  int sz = a.size();
+  auto am = a.get_allocator();
+  const auto& ct = t;
+  int val = convertToInt(t);
+
+  a.resize(n, ct);
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_EQ(n, a.size());
+
+  if (n <= sz) {
+    for (int i = 0; i < n; ++i) {
+      ASSERT_EQ(dsa[i], convertToInt(a[i]));
+    }
+  } else {
+    int i = 0;
+    for ( ; i < sz; ++i) {
+      ASSERT_EQ(dsa[i], convertToInt(a[i]));
+    }
+    for ( ; i < n; ++i) {
+      ASSERT_EQ(val, convertToInt(a[i]));
+    }
+  }
+}
+
+STL_TEST("23.3.6.3", shrinkToFit, is_move_constructible, a) {
+  bool willThrow = Ticker::TicksLeft >= 0;
+
+  a.reserve(a.capacity() * 11);
+
+  auto ocap = a.capacity();
+  DataState<Vector> dsa(a);
+
+  auto am = a.get_allocator();
+
+  try {
+    a.shrink_to_fit();
+  } catch (...) {
+    FAIL() << "shrink_to_fit should swallow errors";
+  }
+
+  ASSERT_TRUE(am == a.get_allocator());
+  ASSERT_TRUE(dsa == a);
+  if (willThrow) {
+    //ASSERT_EQ(ocap, a.capacity()); might shrink in place
+    throw TickException("I swallowed the error");
+  } else {
+    ASSERT_TRUE(a.capacity() == 0 || a.capacity() < ocap) << "Look into this";
+  }
+}
+
+#ifndef USING_STD_VECTOR
+STL_TEST("EBO", ebo, is_destructible) {
+  static_assert(!is_same<Allocator, std::allocator<T>>::value ||
+                sizeof(Vector) == 3 * sizeof(void*),
+    "fbvector has default allocator, but has size != 3*sizeof(void*)");
+}
+
+STL_TEST("relinquish", relinquish, is_destructible, a) {
+  auto sz = a.size();
+  auto cap = a.capacity();
+  auto data = a.data();
+
+  auto guts = relinquish(a);
+
+  ASSERT_EQ(data, guts);
+  ASSERT_TRUE(a.empty());
+  ASSERT_EQ(0, a.capacity());
+
+  auto alloc = a.get_allocator();
+  for (size_t i = 0; i < sz; ++i)
+    std::allocator_traits<decltype(alloc)>::destroy(alloc, guts + i);
+  if (guts != nullptr)
+    std::allocator_traits<decltype(alloc)>::deallocate(alloc, guts, cap);
+}
+
+STL_TEST("attach", attach, is_destructible, a) {
+  DataState<Vector> dsa(a);
+
+  auto sz = a.size();
+  auto cap = a.capacity();
+  auto guts = relinquish(a);
+
+  ASSERT_EQ(a.data(), nullptr);
+  attach(a, guts, sz, cap);
+
+  ASSERT_TRUE(dsa == a);
+}
+
+#endif
+
+// #endif
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  return RUN_ALL_TESTS();
+}
+
+#else // GCC 4.7 guard
+
+// do nothing
+TEST(placeholder, gccversion) {}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  return RUN_ALL_TESTS();
+}
+
+#endif // GCC 4.7 guard