improve io::Cursor read() performance for small sizeof(T)
[folly.git] / folly / FBString.h
index 6912e0c751e05d0d2aa8f1d56e2eaedba14aa1ac..6190230c15e138c39ece5a1d91474957df37c766 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #ifndef FOLLY_BASE_FBSTRING_H_
 #define FOLLY_BASE_FBSTRING_H_
 
-/**
-   fbstring's behavior can be configured via two macro definitions, as
-   follows. Normally, fbstring does not write a '\0' at the end of
-   each string whenever it changes the underlying characters. Instead,
-   it lazily writes the '\0' whenever either c_str() or data()
-   called.
-
-   This is standard-compliant behavior and may save costs in some
-   circumstances. However, it may be surprising to some client code
-   because c_str() and data() are const member functions (fbstring
-   uses the "mutable" storage class for its own state).
-
-   In order to appease client code that expects fbstring to be
-   zero-terminated at all times, if the preprocessor symbol
-   FBSTRING_CONSERVATIVE is defined, fbstring does exactly that,
-   i.e. it goes the extra mile to guarantee a '\0' is always planted
-   at the end of its data.
-
-   On the contrary, if the desire is to debug faulty client code that
-   unduly assumes the '\0' is present, fbstring plants a '^' (i.e.,
-   emphatically NOT a zero) at the end of each string if
-   FBSTRING_PERVERSE is defined. (Calling c_str() or data() still
-   writes the '\0', of course.)
-
-   The preprocessor symbols FBSTRING_PERVERSE and
-   FBSTRING_CONSERVATIVE cannot be defined simultaneously. This is
-   enforced during preprocessing.
-*/
-
-//#define FBSTRING_PERVERSE
-//#define FBSTRING_CONSERVATIVE
-
-#ifdef FBSTRING_PERVERSE
-#ifdef FBSTRING_CONSERVATIVE
-#error Cannot define both FBSTRING_PERVERSE and FBSTRING_CONSERVATIVE.
-#endif
-#endif
-
 #include <atomic>
 #include <limits>
 #include <type_traits>
 
-// libc++ doesn't provide this header
-#ifndef _LIBCPP_VERSION
 // 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
-
+// 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
 // either before or after this inclusion.
 #ifdef FOLLY_MALLOC_H_
 #undef FOLLY_MALLOC_H_
-#include "basic_fbstring_malloc.h"
+#include "basic_fbstring_malloc.h" // nolint
 #else
-#include "basic_fbstring_malloc.h"
+#include "basic_fbstring_malloc.h" // nolint
 #undef FOLLY_MALLOC_H_
 #endif
 
-// FBString cannot use throw when replacing std::string, though it may still
-// use std::__throw_*
-#define throw FOLLY_FBSTRING_MAY_NOT_USE_THROW
-#define THROW_LENGTH_ERROR    std::__throw_length_error
-#define THROW_LOGIC_ERROR     std::__throw_logic_error
-#define THROW_OUT_OF_RANGE    std::__throw_out_of_range
-
 #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 <stdexcept>
+#include <algorithm>
 
-#include "folly/Traits.h"
-#include "folly/Malloc.h"
-#include "folly/Hash.h"
+#include <folly/Traits.h>
+#include <folly/Malloc.h>
+#include <folly/Hash.h>
+#include <folly/ScopeGuard.h>
 
-#define THROW_LENGTH_ERROR    ::folly::throw_length_error
-#define THROW_LOGIC_ERROR     ::folly::throw_logic_error
-#define THROW_OUT_OF_RANGE    ::folly::throw_out_of_range
+#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 {
-namespace {
-
-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);
-}
-
-} // namespace fbstring_detail
 #endif
 
 // Different versions of gcc/clang support different versions of
@@ -174,7 +128,6 @@ OutIt copy_n(InIt b,
              typename std::iterator_traits<InIt>::difference_type n,
              OutIt d) {
   for (; n != 0; --n, ++b, ++d) {
-    assert((const void*)&*d != &*b);
     *d = *b;
   }
   return d;
@@ -341,16 +294,19 @@ public:
     ml_.capacity_ = maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)));
     // or: setSmallSize(0);
     writeTerminator();
-    assert(category() == isSmall && size() == 0);
+    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() == isSmall) {
-      assert(offsetof(MediumLarge, data_) == 0);
-      assert(offsetof(MediumLarge, size_) == sizeof(ml_.data_));
-      assert(offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_));
+    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_;
@@ -363,12 +319,12 @@ public:
         // ml_.capacity field).
         ml_ = rhs.ml_;
       }
-      assert(category() == isSmall && this->size() == rhs.size());
-    } else if (rhs.category() == isLarge) {
+      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() == isLarge && size() == rhs.size());
+      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.
@@ -382,15 +338,16 @@ public:
       // No need for writeTerminator() here, we copied one extra
       // element just above.
       ml_.size_ = rhs.ml_.size_;
-      ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
-      assert(category() == isMedium);
+      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() == isSmall) {
+    if (goner.category() == Category::isSmall) {
       // Just copy, leave the goner in peace
       new(this) fbstring_core(goner.small_, goner.smallSize());
     } else {
@@ -406,13 +363,25 @@ public:
   // 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));
-      /*static_*/assert(sizeof(Char*) == sizeof(size_t));
+      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);
+      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.
@@ -437,6 +406,7 @@ public:
         }
       }
       setSmallSize(size);
+      return;
     } else if (size <= maxMediumSize) {
       // Medium strings are allocated normally. Don't forget to
       // allocate one extra Char for the terminating null.
@@ -444,26 +414,26 @@ public:
       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) | isMedium;
+      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 | isLarge;
+      ml_.capacity_ = effectiveCapacity
+                      | static_cast<category_type>(Category::isLarge);
     }
     writeTerminator();
-    assert(this->size() == size);
-    assert(memcmp(this->data(), data, size * sizeof(Char)) == 0);
   }
 
   ~fbstring_core() noexcept {
     auto const c = category();
-    if (c == isSmall) {
+    if (c == Category::isSmall) {
       return;
     }
-    if (c == isMedium) {
+    if (c == Category::isMedium) {
       free(ml_.data_);
       return;
     }
@@ -488,7 +458,8 @@ public:
       ml_.data_ = data;
       ml_.size_ = size;
       // Don't forget about null terminator
-      ml_.capacity_ = (allocatedSize - 1) | isMedium;
+      ml_.capacity_ = (allocatedSize - 1)
+                      | static_cast<category_type>(Category::isMedium);
     } else {
       // No need for the memory
       free(data);
@@ -513,11 +484,11 @@ public:
 
   Char * mutable_data() {
     auto const c = category();
-    if (c == isSmall) {
+    if (c == Category::isSmall) {
       return small_;
     }
-    assert(c == isMedium || c == isLarge);
-    if (c == isLarge && RefCounted::refs(ml_.data_) > 1) {
+    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);
@@ -535,44 +506,27 @@ public:
 
   const Char * c_str() const {
     auto const c = category();
-#ifdef FBSTRING_PERVERSE
-    if (c == isSmall) {
-      assert(small_[smallSize()] == TERMINATOR || smallSize() == maxSmallSize
-             || small_[smallSize()] == '\0');
-      small_[smallSize()] = '\0';
-      return small_;
-    }
-    assert(c == isMedium || c == isLarge);
-    assert(ml_.data_[ml_.size_] == TERMINATOR || ml_.data_[ml_.size_] == '\0');
-    ml_.data_[ml_.size_] = '\0';
-#elif defined(FBSTRING_CONSERVATIVE)
-    if (c == isSmall) {
+    if (c == Category::isSmall) {
       assert(small_[smallSize()] == '\0');
       return small_;
     }
-    assert(c == isMedium || c == isLarge);
+    assert(c == Category::isMedium || c == Category::isLarge);
     assert(ml_.data_[ml_.size_] == '\0');
-#else
-    if (c == isSmall) {
-      small_[smallSize()] = '\0';
-      return small_;
-    }
-    assert(c == isMedium || c == isLarge);
-    ml_.data_[ml_.size_] = '\0';
-#endif
     return ml_.data_;
   }
 
   void shrink(const size_t delta) {
-    if (category() == isSmall) {
+    if (category() == Category::isSmall) {
       // Check for underflow
       assert(delta <= smallSize());
       setSmallSize(smallSize() - delta);
-    } else if (category() == isMedium || RefCounted::refs(ml_.data_) == 1) {
+    } 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
@@ -582,13 +536,11 @@ public:
         fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this);
       }
       // No need to write the terminator.
-      return;
     }
-    writeTerminator();
   }
 
   void reserve(size_t minCapacity) {
-    if (category() == isLarge) {
+    if (category() == Category::isLarge) {
       // Ensure unique
       if (RefCounted::refs(ml_.data_) > 1) {
         // We must make it unique regardless; in-place reallocation is
@@ -604,7 +556,8 @@ public:
         // we have + 1 above.
         RefCounted::decrementRefs(ml_.data_);
         ml_.data_ = newRC->data_;
-        ml_.capacity_ = minCapacity | isLarge;
+        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)
@@ -614,12 +567,13 @@ public:
                RefCounted::reallocate(ml_.data_, ml_.size_,
                                       ml_.capacity(), minCapacity);
           ml_.data_ = newRC->data_;
-          ml_.capacity_ = minCapacity | isLarge;
+          ml_.capacity_ = minCapacity
+                          | static_cast<category_type>(Category::isLarge);
           writeTerminator();
         }
         assert(capacity() >= minCapacity);
       }
-    } else if (category() == isMedium) {
+    } else if (category() == Category::isMedium) {
       // String is not shared
       if (minCapacity <= ml_.capacity()) {
         return; // nothing to do, there's enough room
@@ -635,7 +589,8 @@ public:
             (ml_.capacity() + 1) * sizeof(Char),
             capacityBytes));
         writeTerminator();
-        ml_.capacity_ = (capacityBytes / sizeof(Char) - 1) | isMedium;
+        ml_.capacity_ = (capacityBytes / sizeof(Char) - 1)
+                        | static_cast<category_type>(Category::isMedium);
       } else {
         // Conversion from medium to large string
         fbstring_core nascent;
@@ -649,7 +604,7 @@ public:
         assert(capacity() >= minCapacity);
       }
     } else {
-      assert(category() == isSmall);
+      assert(category() == Category::isSmall);
       if (minCapacity > maxMediumSize) {
         // large
         auto const newRC = RefCounted::create(& minCapacity);
@@ -658,7 +613,8 @@ public:
         // No need for writeTerminator(), we wrote it above with + 1.
         ml_.data_ = newRC->data_;
         ml_.size_ = size;
-        ml_.capacity_ = minCapacity | isLarge;
+        ml_.capacity_ = minCapacity
+                        | static_cast<category_type>(Category::isLarge);
         assert(capacity() >= minCapacity);
       } else if (minCapacity > maxSmallSize) {
         // medium
@@ -671,7 +627,8 @@ public:
         // No need for writeTerminator(), we wrote it above with + 1.
         ml_.data_ = data;
         ml_.size_ = size;
-        ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1) | isMedium;
+        ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1)
+                        | static_cast<category_type>(Category::isMedium);
       } else {
         // small
         // Nothing to do, everything stays put
@@ -684,12 +641,11 @@ public:
     // Strategy is simple: make room, then change size
     assert(capacity() >= size());
     size_t sz, newSz;
-    if (category() == isSmall) {
+    if (category() == Category::isSmall) {
       sz = smallSize();
       newSz = sz + delta;
       if (newSz <= maxSmallSize) {
         setSmallSize(newSz);
-        writeTerminator();
         return small_ + sz;
       }
       reserve(newSz);
@@ -702,7 +658,7 @@ public:
     }
     assert(capacity() >= newSz);
     // Category can't be small - we took care of that above
-    assert(category() == isMedium || category() == isLarge);
+    assert(category() == Category::isMedium || category() == Category::isLarge);
     ml_.size_ = newSz;
     writeTerminator();
     assert(size() == newSz);
@@ -712,12 +668,11 @@ public:
   void push_back(Char c) {
     assert(capacity() >= size());
     size_t sz;
-    if (category() == isSmall) {
+    if (category() == Category::isSmall) {
       sz = smallSize();
       if (sz < maxSmallSize) {
-        setSmallSize(sz + 1);
         small_[sz] = c;
-        writeTerminator();
+        setSmallSize(sz + 1);
         return;
       }
       reserve(maxSmallSize * 2);
@@ -730,21 +685,21 @@ public:
     assert(!isShared());
     assert(capacity() >= sz + 1);
     // Category can't be small - we took care of that above
-    assert(category() == isMedium || category() == isLarge);
+    assert(category() == Category::isMedium || category() == Category::isLarge);
     ml_.size_ = sz + 1;
     ml_.data_[sz] = c;
     writeTerminator();
   }
 
   size_t size() const {
-    return category() == isSmall ? smallSize() : ml_.size_;
+    return category() == Category::isSmall ? smallSize() : ml_.size_;
   }
 
   size_t capacity() const {
     switch (category()) {
-      case isSmall:
+      case Category::isSmall:
         return maxSmallSize;
-      case isLarge:
+      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.
@@ -755,26 +710,18 @@ public:
   }
 
   bool isShared() const {
-    return category() == isLarge && RefCounted::refs(ml_.data_) > 1;
+    return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1;
   }
 
-#ifdef FBSTRING_PERVERSE
-  enum { TERMINATOR = '^' };
-#else
-  enum { TERMINATOR = '\0' };
-#endif
-
   void writeTerminator() {
-#if defined(FBSTRING_PERVERSE) || defined(FBSTRING_CONSERVATIVE)
-    if (category() == isSmall) {
+    if (category() == Category::isSmall) {
       const auto s = smallSize();
       if (s != maxSmallSize) {
-        small_[s] = TERMINATOR;
+        small_[s] = '\0';
       }
     } else {
-      ml_.data_[ml_.size_] = TERMINATOR;
+      ml_.data_[ml_.size_] = '\0';
     }
-#endif
   }
 
 private:
@@ -859,8 +806,8 @@ private:
   };
 
   union {
-    mutable Char small_[sizeof(MediumLarge) / sizeof(Char)];
-    mutable MediumLarge ml_;
+    Char small_[sizeof(MediumLarge) / sizeof(Char)];
+    MediumLarge ml_;
   };
 
   enum {
@@ -874,7 +821,10 @@ private:
   static_assert(!(sizeof(MediumLarge) % sizeof(Char)),
                 "Corrupt memory layout for fbstring.");
 
-  enum Category {
+  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,
@@ -886,7 +836,9 @@ private:
   }
 
   size_t smallSize() const {
-    assert(category() == isSmall && small_[maxSmallSize] <= maxSmallSize);
+    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]);
   }
@@ -897,6 +849,7 @@ private:
     // small_[maxSmallSize].
     assert(s <= maxSmallSize);
     small_[maxSmallSize] = maxSmallSize - s;
+    writeTerminator();
   }
 };
 
@@ -990,7 +943,7 @@ class basic_fbstring {
       size() <= max_size() &&
       capacity() <= max_size() &&
       size() <= capacity() &&
-      (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0');
+      begin()[size()] == '\0';
   }
 
   struct Invariant;
@@ -1071,12 +1024,11 @@ public:
   }
 
   /* implicit */ basic_fbstring(const value_type* s, const A& a = A())
-      : store_(s, s ? traits_type::length(s) : ({
-          basic_fbstring<char> err = __PRETTY_FUNCTION__;
-          err += ": null pointer initializer not valid";
-          THROW_LOGIC_ERROR(err.c_str());
-          0;
-      })) {
+      : 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())
@@ -1287,7 +1239,7 @@ public:
   size_type capacity() const { return store_.capacity(); }
 
   void reserve(size_type res_arg = 0) {
-    enforce(res_arg <= max_size(), THROW_LENGTH_ERROR, "");
+    enforce(res_arg <= max_size(), std::__throw_length_error, "");
     store_.reserve(res_arg);
   }
 
@@ -1305,24 +1257,20 @@ public:
 
   // C++11 21.4.5 element access:
   const_reference operator[](size_type pos) const {
-    return *(c_str() + pos);
+    return *(begin() + pos);
   }
 
   reference operator[](size_type pos) {
-    if (pos == size()) {
-      // Just call c_str() to make sure '\0' is present
-      c_str();
-    }
     return *(begin() + pos);
   }
 
   const_reference at(size_type n) const {
-    enforce(n <= size(), THROW_OUT_OF_RANGE, "");
+    enforce(n <= size(), std::__throw_out_of_range, "");
     return (*this)[n];
   }
 
   reference at(size_type n) {
-    enforce(n < size(), THROW_OUT_OF_RANGE, "");
+    enforce(n < size(), std::__throw_out_of_range, "");
     return (*this)[n];
   }
 
@@ -1357,7 +1305,7 @@ public:
   basic_fbstring& append(const basic_fbstring& str, const size_type pos,
                          size_type n) {
     const size_type sz = str.size();
-    enforce(pos <= sz, THROW_OUT_OF_RANGE, "");
+    enforce(pos <= sz, std::__throw_out_of_range, "");
     procrustes(n, sz - pos);
     return append(str.data() + pos, n);
   }
@@ -1434,7 +1382,7 @@ public:
   basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
                          size_type n) {
     const size_type sz = str.size();
-    enforce(pos <= sz, THROW_OUT_OF_RANGE, "");
+    enforce(pos <= sz, std::__throw_out_of_range, "");
     procrustes(n, sz - pos);
     return assign(str.data() + pos, n);
   }
@@ -1476,13 +1424,13 @@ public:
 
   basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
                          size_type pos2, size_type n) {
-    enforce(pos2 <= str.length(), THROW_OUT_OF_RANGE, "");
+    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(), THROW_OUT_OF_RANGE, "");
+    enforce(pos <= length(), std::__throw_out_of_range, "");
     insert(begin() + pos, s, s + n);
     return *this;
   }
@@ -1492,7 +1440,7 @@ public:
   }
 
   basic_fbstring& insert(size_type pos, size_type n, value_type c) {
-    enforce(pos <= length(), THROW_OUT_OF_RANGE, "");
+    enforce(pos <= length(), std::__throw_out_of_range, "");
     insert(begin() + pos, n, c);
     return *this;
   }
@@ -1612,7 +1560,7 @@ public:
   basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
     Invariant checker(*this);
     (void) checker;
-    enforce(pos <= length(), THROW_OUT_OF_RANGE, "");
+    enforce(pos <= length(), std::__throw_out_of_range, "");
     procrustes(n, length() - pos);
     std::copy(begin() + pos + n, end(), begin() + pos);
     resize(length() - n);
@@ -1621,7 +1569,7 @@ public:
 
   iterator erase(iterator position) {
     const size_type pos(position - begin());
-    enforce(pos <= size(), THROW_OUT_OF_RANGE, "");
+    enforce(pos <= size(), std::__throw_out_of_range, "");
     erase(pos, 1);
     return begin() + pos;
   }
@@ -1644,7 +1592,7 @@ public:
   basic_fbstring& replace(size_type pos1, size_type n1,
                           const basic_fbstring& str,
                           size_type pos2, size_type n2) {
-    enforce(pos2 <= str.length(), THROW_OUT_OF_RANGE, "");
+    enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
     return replace(pos1, n1, str.data() + pos2,
                    std::min(n2, str.size() - pos2));
   }
@@ -1666,7 +1614,7 @@ public:
                           StrOrLength s_or_n2, NumOrChar n_or_c) {
     Invariant checker(*this);
     (void) checker;
-    enforce(pos <= size(), THROW_OUT_OF_RANGE, "");
+    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);
@@ -1714,15 +1662,15 @@ private:
   }
 
 private:
-  template <class FwdIterator, class P>
+  template <class FwdIterator>
   bool replaceAliased(iterator i1, iterator i2,
-                      FwdIterator s1, FwdIterator s2, P*) {
+                      FwdIterator s1, FwdIterator s2, std::false_type) {
     return false;
   }
 
   template <class FwdIterator>
   bool replaceAliased(iterator i1, iterator i2,
-                      FwdIterator s1, FwdIterator s2, value_type*) {
+                      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());
@@ -1737,7 +1685,6 @@ private:
     return true;
   }
 
-public:
   template <class FwdIterator>
   void replaceImpl(iterator i1, iterator i2,
                    FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
@@ -1745,7 +1692,10 @@ public:
     (void) checker;
 
     // Handle aliased replace
-    if (replaceAliased(i1, i2, s1, s2, &*s1)) {
+    if (replaceAliased(i1, i2, s1, s2,
+          std::integral_constant<bool,
+            std::is_same<FwdIterator, iterator>::value ||
+            std::is_same<FwdIterator, const_iterator>::value>())) {
       return;
     }
 
@@ -1788,7 +1738,7 @@ public:
   }
 
   size_type copy(value_type* s, size_type n, size_type pos = 0) const {
-    enforce(pos <= size(), THROW_OUT_OF_RANGE, "");
+    enforce(pos <= size(), std::__throw_out_of_range, "");
     procrustes(n, size() - pos);
 
     fbstring_detail::pod_copy(
@@ -2020,7 +1970,7 @@ public:
   }
 
   basic_fbstring substr(size_type pos = 0, size_type n = npos) const {
-    enforce(pos <= size(), THROW_OUT_OF_RANGE, "");
+    enforce(pos <= size(), std::__throw_out_of_range, "");
     return basic_fbstring(data() + pos, std::min(n, size() - pos));
   }
 
@@ -2041,7 +1991,7 @@ public:
 
   int compare(size_type pos1, size_type n1,
               const value_type* s, size_type n2) const {
-    enforce(pos1 <= size(), THROW_OUT_OF_RANGE, "");
+    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));
@@ -2051,7 +2001,7 @@ public:
   int compare(size_type pos1, size_type n1,
               const basic_fbstring& str,
               size_type pos2, size_type n2) const {
-    enforce(pos2 <= str.size(), THROW_OUT_OF_RANGE, "");
+    enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
     return compare(pos1, n1, str.data() + pos2,
                    std::min(n2, str.size() - pos2));
   }
@@ -2335,7 +2285,29 @@ 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) {
-  os.write(str.data(), str.size());
+#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);
+    }
+  }
+#else
+  std::__ostream_insert(os, str.data(), str.size());
+#endif
   return os;
 }
 
@@ -2351,7 +2323,7 @@ getline(
     basic_fbstring<E, T, A, S>& str,
   typename basic_fbstring<E, T, A, S>::value_type delim) {
   // Use the nonstandard getdelim()
-  char * buf = NULL;
+  char * buf = nullptr;
   size_t size = 0;
   for (;;) {
     // This looks quadratic but it really depends on realloc
@@ -2440,21 +2412,51 @@ _GLIBCXX_END_NAMESPACE_VERSION
 
 } // namespace folly
 
-#pragma GCC diagnostic pop
-
 #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 {
-template <>
-struct hash< ::folly::fbstring> {
-  size_t operator()(const ::folly::fbstring& s) const {
-    return ::folly::hash::fnv32_buf(s.data(), s.size());
-  }
-};
-}
+
+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