Fix UBSAN failure in RangeTest.cpp with libstdc++-v4.9
[folly.git] / folly / FBString.h
index 00d1a959978cc6df28ed2d74ba75ab7a4585f568..4882aac577650dc0689c91baa9269e5bf4438390 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@
 #pragma once
 
 #include <atomic>
+#include <cstddef>
+#include <iosfwd>
 #include <limits>
 #include <type_traits>
 
 
 #pragma GCC system_header
 
-#include "basic_fbstring_malloc.h"
+#include "basic_fbstring_malloc.h" // @manual
 
 // When used as std::string replacement always disable assertions.
 #define FBSTRING_ASSERT(expr) /* empty */
 
 #else // !_LIBSTDCXX_FBSTRING
 
+#include <folly/CppAttributes.h>
 #include <folly/Portability.h>
 
 // libc++ doesn't provide this header, nor does msvc
 #include <string>
 #include <utility>
 
-#include <folly/Hash.h>
-#include <folly/Malloc.h>
 #include <folly/Traits.h>
-
-#if FOLLY_HAVE_DEPRECATED_ASSOC
-#ifdef _GLIBCXX_SYMVER
-#include <ext/hash_set>
-#include <ext/hash_map>
-#endif
-#endif
+#include <folly/hash/Hash.h>
+#include <folly/memory/Malloc.h>
+#include <folly/portability/BitsFunctexcept.h>
 
 // When used in folly, assertions are not disabled.
 #define FBSTRING_ASSERT(expr) assert(expr)
 #define FBSTRING_UNLIKELY(x) (x)
 #endif
 
-#pragma GCC diagnostic push
+FOLLY_PUSH_WARNING
 // Ignore shadowing warnings within this file, so includers can use -Wshadow.
-#pragma GCC diagnostic ignored "-Wshadow"
+FOLLY_GCC_DISABLE_WARNING("-Wshadow")
 // GCC 4.9 has a false positive in setSmallSize (probably
 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124), disable
 // compile-time array bound checking.
-#pragma GCC diagnostic ignored "-Warray-bounds"
+FOLLY_GCC_DISABLE_WARNING("-Warray-bounds")
 
 // FBString cannot use throw when replacing std::string, though it may still
 // use std::__throw_*
 #define throw FOLLY_FBSTRING_MAY_NOT_USE_THROW
 
 #ifdef _LIBSTDCXX_FBSTRING
-namespace std _GLIBCXX_VISIBILITY(default) {
-_GLIBCXX_BEGIN_NAMESPACE_VERSION
+#define FOLLY_FBSTRING_BEGIN_NAMESPACE         \
+  namespace std _GLIBCXX_VISIBILITY(default) { \
+    _GLIBCXX_BEGIN_NAMESPACE_VERSION
+#define FOLLY_FBSTRING_END_NAMESPACE \
+  _GLIBCXX_END_NAMESPACE_VERSION     \
+  } // namespace std
 #else
-namespace folly {
+#define FOLLY_FBSTRING_BEGIN_NAMESPACE namespace folly {
+#define FOLLY_FBSTRING_END_NAMESPACE } // namespace folly
 #endif
 
+FOLLY_FBSTRING_BEGIN_NAMESPACE
+
 #if defined(__clang__)
 # if __has_feature(address_sanitizer)
 #  define FBSTRING_SANITIZE_ADDRESS
@@ -135,8 +139,9 @@ inline std::pair<InIt, OutIt> copy_n(
 template <class Pod, class T>
 inline void podFill(Pod* b, Pod* e, T c) {
   FBSTRING_ASSERT(b && e && b <= e);
-  /*static*/ if (sizeof(T) == 1) {
-    memset(b, c, e - b);
+  constexpr auto kUseMemset = sizeof(T) == 1;
+  /* static */ if (kUseMemset) {
+    memset(b, c, size_t(e - b));
   } else {
     auto const ee = b + ((e - b) & ~7u);
     for (; b != ee; b += 8) {
@@ -226,7 +231,7 @@ enum class AcquireMallocatedString {};
 
 template <class Char>
 class fbstring_core_model {
-public:
+ public:
   fbstring_core_model();
   fbstring_core_model(const fbstring_core_model &);
   ~fbstring_core_model();
@@ -275,7 +280,7 @@ public:
   // the string without reallocation. For reference-counted strings,
   // it should fork the data even if minCapacity < size().
   void reserve(size_t minCapacity);
-private:
+ private:
   // Do not implement
   fbstring_core_model& operator=(const fbstring_core_model &);
 };
@@ -289,13 +294,12 @@ private:
  * 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 (> 23 chars) are stored in
- * malloc-allocated memory that is copied eagerly.
- * There exists a third storage category: (c) "large", which has the
- * copy-on-write optimization. COW was disallowed in C++11, so large is
- * now deprecated in fbstring_core. fbstring_core no longer creates large
- * strings, though still works with them. Later, large strings will be
- * completely removed.
+ * 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 two
  * bits of the rightmost char of the storage. If neither is set, then the
@@ -309,7 +313,7 @@ private:
  * to extract capacity/category.
  */
 template <class Char> class fbstring_core {
-protected:
+ protected:
 // It's MSVC, so we just have to guess ... and allow an override
 #ifdef _MSC_VER
 # ifdef FOLLY_ENDIAN_BE
@@ -321,15 +325,23 @@ protected:
   static constexpr auto kIsLittleEndian =
       __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__;
 #endif
-public:
+ public:
   fbstring_core() noexcept { reset(); }
 
   fbstring_core(const fbstring_core & rhs) {
     FBSTRING_ASSERT(&rhs != this);
-    if (rhs.category() == Category::isSmall) {
-      makeSmall(rhs);
-    } else {
-      makeMedium(rhs);
+    switch (rhs.category()) {
+      case Category::isSmall:
+        copySmall(rhs);
+        break;
+      case Category::isMedium:
+        copyMedium(rhs);
+        break;
+      case Category::isLarge:
+        copyLarge(rhs);
+        break;
+      default:
+        fbstring_detail::assume_unreachable();
     }
     FBSTRING_ASSERT(size() == rhs.size());
     FBSTRING_ASSERT(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
@@ -347,8 +359,10 @@ public:
                 bool disableSSO = FBSTRING_DISABLE_SSO) {
     if (!disableSSO && size <= maxSmallSize) {
       initSmall(data, size);
-    } else {
+    } else if (size <= maxMediumSize) {
       initMedium(data, size);
+    } else {
+      initLarge(data, size);
     }
     FBSTRING_ASSERT(this->size() == size);
     FBSTRING_ASSERT(
@@ -487,7 +501,9 @@ public:
         if (RefCounted::refs(ml_.data_) > 1) {
           return ml_.size_;
         }
-      default: {}
+        break;
+      default:
+        break;
     }
     return ml_.capacity();
   }
@@ -496,7 +512,7 @@ public:
     return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1;
   }
 
-private:
+ private:
   // Disabled
   fbstring_core & operator=(const fbstring_core & rhs);
 
@@ -518,11 +534,14 @@ private:
     std::atomic<size_t> refCount_;
     Char data_[1];
 
+    constexpr static size_t getDataOffset() {
+      return offsetof(RefCounted, data_);
+    }
+
     static RefCounted * fromData(Char * p) {
-      return static_cast<RefCounted*>(
-        static_cast<void*>(
-          static_cast<unsigned char*>(static_cast<void*>(p))
-          - sizeof(refCount_)));
+      return static_cast<RefCounted*>(static_cast<void*>(
+          static_cast<unsigned char*>(static_cast<void*>(p)) -
+          getDataOffset()));
     }
 
     static size_t refs(Char * p) {
@@ -543,14 +562,11 @@ private:
     }
 
     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));
+      const size_t allocSize =
+          goodMallocSize(getDataOffset() + (*size + 1) * sizeof(Char));
       auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));
       result->refCount_.store(1, std::memory_order_release);
-      *size = (allocSize - sizeof(RefCounted)) / sizeof(Char);
+      *size = (allocSize - getDataOffset()) / sizeof(Char) - 1;
       return result;
     }
 
@@ -566,19 +582,19 @@ private:
     static RefCounted * reallocate(Char *const data,
                                    const size_t currentSize,
                                    const size_t currentCapacity,
-                                   const size_t newCapacity) {
-      FBSTRING_ASSERT(newCapacity > 0 && newCapacity > currentSize);
+                                   size_t * newCapacity) {
+      FBSTRING_ASSERT(*newCapacity > 0 && *newCapacity > currentSize);
+      const size_t allocNewCapacity =
+          goodMallocSize(getDataOffset() + (*newCapacity + 1) * sizeof(Char));
       auto const dis = fromData(data);
       FBSTRING_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)));
+      auto result = static_cast<RefCounted*>(smartRealloc(
+          dis,
+          getDataOffset() + (currentSize + 1) * sizeof(Char),
+          getDataOffset() + (currentCapacity + 1) * sizeof(Char),
+          allocNewCapacity));
       FBSTRING_ASSERT(result->refCount_.load(std::memory_order_acquire) == 1);
+      *newCapacity = (allocNewCapacity - getDataOffset()) / sizeof(Char) - 1;
       return result;
     }
   };
@@ -608,7 +624,6 @@ private:
     }
 
     void setCapacity(size_t cap, Category cat) {
-      FBSTRING_ASSERT(cat != Category::isLarge);
       capacity_ = kIsLittleEndian
           ? cap | (static_cast<size_t>(cat) << kCategoryShift)
           : (cap << 2) | static_cast<size_t>(cat);
@@ -647,14 +662,14 @@ private:
     // small_[maxSmallSize].
     FBSTRING_ASSERT(s <= maxSmallSize);
     constexpr auto shift = kIsLittleEndian ? 0 : 2;
-    small_[maxSmallSize] = (maxSmallSize - s) << shift;
+    small_[maxSmallSize] = char((maxSmallSize - s) << shift);
     small_[s] = '\0';
     FBSTRING_ASSERT(category() == Category::isSmall && size() == s);
   }
 
-  void makeSmall(const fbstring_core&);
-  void makeMedium(const fbstring_core&);
-  void makeLarge(const fbstring_core&);
+  void copySmall(const fbstring_core&);
+  void copyMedium(const fbstring_core&);
+  void copyLarge(const fbstring_core&);
 
   void initSmall(const Char* data, size_t size);
   void initMedium(const Char* data, size_t size);
@@ -673,7 +688,7 @@ private:
 };
 
 template <class Char>
-inline void fbstring_core<Char>::makeSmall(const fbstring_core& rhs) {
+inline void fbstring_core<Char>::copySmall(const fbstring_core& rhs) {
   static_assert(offsetof(MediumLarge, data_) == 0, "fbstring layout failure");
   static_assert(
       offsetof(MediumLarge, size_) == sizeof(ml_.data_),
@@ -692,7 +707,7 @@ inline void fbstring_core<Char>::makeSmall(const fbstring_core& rhs) {
 }
 
 template <class Char>
-FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::makeMedium(
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::copyMedium(
     const fbstring_core& rhs) {
   // Medium strings are copied eagerly. Don't forget to allocate
   // one extra Char for the null terminator.
@@ -707,7 +722,7 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::makeMedium(
 }
 
 template <class Char>
-FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::makeLarge(
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::copyLarge(
     const fbstring_core& rhs) {
   // Large strings are just refcounted
   ml_ = rhs.ml_;
@@ -742,10 +757,13 @@ inline void fbstring_core<Char>::initSmall(
     switch ((byteSize + wordWidth - 1) / wordWidth) { // Number of words.
       case 3:
         ml_.capacity_ = reinterpret_cast<const size_t*>(data)[2];
+        FOLLY_FALLTHROUGH;
       case 2:
         ml_.size_ = reinterpret_cast<const size_t*>(data)[1];
+        FOLLY_FALLTHROUGH;
       case 1:
         ml_.data_ = *reinterpret_cast<Char**>(const_cast<Char*>(data));
+        FOLLY_FALLTHROUGH;
       case 0:
         break;
     }
@@ -828,7 +846,7 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::reserveLarge(
     if (minCapacity > ml_.capacity()) {
       // Asking for more memory
       auto const newRC = RefCounted::reallocate(
-          ml_.data_, ml_.size_, ml_.capacity(), minCapacity);
+          ml_.data_, ml_.size_, ml_.capacity(), &minCapacity);
       ml_.data_ = newRC->data_;
       ml_.setCapacity(minCapacity, Category::isLarge);
     }
@@ -844,16 +862,29 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::reserveMedium(
   if (minCapacity <= ml_.capacity()) {
     return; // nothing to do, there's enough room
   }
-  // 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));
-  // Also copies terminator.
-  ml_.data_ = static_cast<Char*>(smartRealloc(
-      ml_.data_,
-      (ml_.size_ + 1) * sizeof(Char),
-      (ml_.capacity() + 1) * sizeof(Char),
-      capacityBytes));
-  ml_.setCapacity(capacityBytes / sizeof(Char) - 1, Category::isMedium);
+  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));
+    // Also copies terminator.
+    ml_.data_ = static_cast<Char*>(smartRealloc(
+        ml_.data_,
+        (ml_.size_ + 1) * sizeof(Char),
+        (ml_.capacity() + 1) * sizeof(Char),
+        capacityBytes));
+    ml_.setCapacity(capacityBytes / sizeof(Char) - 1, 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_;
+    // Also copies terminator.
+    fbstring_detail::podCopy(
+        ml_.data_, ml_.data_ + ml_.size_ + 1, nascent.ml_.data_);
+    nascent.swap(*this);
+    FBSTRING_ASSERT(capacity() >= minCapacity);
+  }
 }
 
 template <class Char>
@@ -863,17 +894,29 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::reserveSmall(
   if (!disableSSO && minCapacity <= maxSmallSize) {
     // small
     // Nothing to do, everything stays put
-    return;
+  } else if (minCapacity <= maxMediumSize) {
+    // medium
+    // Don't forget to allocate one extra Char for the terminating null
+    auto const allocSizeBytes =
+        goodMallocSize((1 + minCapacity) * sizeof(Char));
+    auto const pData = static_cast<Char*>(checkedMalloc(allocSizeBytes));
+    auto const size = smallSize();
+    // Also copies terminator.
+    fbstring_detail::podCopy(small_, small_ + size + 1, pData);
+    ml_.data_ = pData;
+    ml_.size_ = size;
+    ml_.setCapacity(allocSizeBytes / sizeof(Char) - 1, Category::isMedium);
+  } else {
+    // large
+    auto const newRC = RefCounted::create(&minCapacity);
+    auto const size = smallSize();
+    // Also copies terminator.
+    fbstring_detail::podCopy(small_, small_ + size + 1, newRC->data_);
+    ml_.data_ = newRC->data_;
+    ml_.size_ = size;
+    ml_.setCapacity(minCapacity, Category::isLarge);
+    FBSTRING_ASSERT(capacity() >= minCapacity);
   }
-  // Don't forget to allocate one extra Char for the terminating null
-  auto const allocSizeBytes = goodMallocSize((1 + minCapacity) * sizeof(Char));
-  auto const pData = static_cast<Char*>(checkedMalloc(allocSizeBytes));
-  auto const size = smallSize();
-  // Also copies terminator.
-  fbstring_detail::podCopy(small_, small_ + size + 1, pData);
-  ml_.data_ = pData;
-  ml_.size_ = size;
-  ml_.setCapacity(allocSizeBytes / sizeof(Char) - 1, Category::isMedium);
 }
 
 template <class Char>
@@ -946,7 +989,7 @@ inline void fbstring_core<Char>::shrinkLarge(const size_t delta) {
  */
 template <class Char>
 class dummy_fbstring_core {
-public:
+ public:
   dummy_fbstring_core() {
   }
   dummy_fbstring_core(const dummy_fbstring_core& another)
@@ -989,7 +1032,7 @@ public:
     backend_.reserve(minCapacity);
   }
 
-private:
+ private:
   std::basic_string<Char> backend_;
 };
 #endif // !_LIBSTDCXX_FBSTRING
@@ -1002,10 +1045,11 @@ private:
 #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> >
+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(
@@ -1070,7 +1114,7 @@ class basic_fbstring {
   static constexpr size_type npos = size_type(-1);
   typedef std::true_type IsRelocatable;
 
-private:
+ private:
   static void procrustes(size_type& n, size_type nmax) {
     if (n > nmax) {
       n = nmax;
@@ -1079,7 +1123,7 @@ private:
 
   static size_type traitsLength(const value_type* s);
 
-public:
+ public:
   // C++11 21.4.2 construct/copy/destroy
 
   // Note: while the following two constructors can be (and previously were)
@@ -1111,9 +1155,9 @@ public:
 
 #ifndef _LIBSTDCXX_FBSTRING
   // This is defined for compatibility with std::string
-  /* implicit */ basic_fbstring(const std::string& str)
-      : store_(str.data(), str.size()) {
-  }
+  template <typename A2>
+  /* implicit */ basic_fbstring(const std::basic_string<E, T, A2>& str)
+      : store_(str.data(), str.size()) {}
 #endif
 
   basic_fbstring(const basic_fbstring& str,
@@ -1151,7 +1195,7 @@ public:
   // Specialization for const char*, const char*
   FOLLY_MALLOC_NOINLINE
   basic_fbstring(const value_type* b, const value_type* e, const A& /*a*/ = A())
-      : store_(b, e - b) {
+      : store_(b, size_type(e - b)) {
   }
 
   // Nonstandard constructor
@@ -1175,13 +1219,14 @@ public:
 
 #ifndef _LIBSTDCXX_FBSTRING
   // Compatibility with std::string
-  basic_fbstring & operator=(const std::string & rhs) {
+  template <typename A2>
+  basic_fbstring& operator=(const std::basic_string<E, T, A2>& rhs) {
     return assign(rhs.data(), rhs.size());
   }
 
   // Compatibility with std::string
-  std::string toStdString() const {
-    return std::string(data(), size());
+  std::basic_string<E, T, A> toStdString() const {
+    return std::basic_string<E, T, A>(data(), size());
   }
 #else
   // A lot of code in fbcode still uses this method, so keep it here for now.
@@ -1194,7 +1239,24 @@ public:
     return assign(s);
   }
 
-  basic_fbstring& operator=(value_type c);
+  // This actually goes directly against the C++ spec, but the
+  // value_type overload is dangerous, so we're explicitly deleting
+  // any overloads of operator= that could implicitly convert to
+  // value_type.
+  // Note that we do need to explicitly specify the template types because
+  // otherwise MSVC 2017 will aggressively pre-resolve value_type to
+  // traits_type::char_type, which won't compare as equal when determining
+  // which overload the implementation is referring to.
+  // Also note that MSVC 2015 Update 3 requires us to explicitly specify the
+  // namespace in-which to search for basic_fbstring, otherwise it tries to
+  // look for basic_fbstring::basic_fbstring, which is just plain wrong.
+  template <typename TP>
+  typename std::enable_if<
+      std::is_same<
+          typename std::decay<TP>::type,
+          typename folly::basic_fbstring<E, T, A, Storage>::value_type>::value,
+      basic_fbstring<E, T, A, Storage>&>::type
+  operator=(TP c);
 
   basic_fbstring& operator=(std::initializer_list<value_type> il) {
     return assign(il.begin(), il.end());
@@ -1343,7 +1405,7 @@ public:
 
   basic_fbstring& append(size_type n, value_type c);
 
-  template<class InputIterator>
+  template <class InputIterator>
   basic_fbstring& append(InputIterator first, InputIterator last) {
     insert(end(), first, last);
     return *this;
@@ -1358,7 +1420,9 @@ public:
   }
 
   basic_fbstring& assign(const basic_fbstring& str) {
-    if (&str == this) return *this;
+    if (&str == this) {
+      return *this;
+    }
     return assign(str.data(), str.size());
   }
 
@@ -1434,29 +1498,29 @@ public:
   }
 #endif
 
-private:
- iterator
- insertImplDiscr(const_iterator i, size_type n, value_type c, std::true_type);
-
- template <class InputIter>
- iterator
- insertImplDiscr(const_iterator i, InputIter b, InputIter e, std::false_type);
-
- template <class FwdIterator>
- iterator insertImpl(
-     const_iterator i,
-     FwdIterator s1,
-     FwdIterator s2,
-     std::forward_iterator_tag);
-
- template <class InputIterator>
- iterator insertImpl(
-     const_iterator i,
-     InputIterator b,
-     InputIterator e,
-     std::input_iterator_tag);
-
-public:
+ private:
 iterator
 insertImplDiscr(const_iterator i, size_type n, value_type c, std::true_type);
+
 template <class InputIter>
 iterator
 insertImplDiscr(const_iterator i, InputIter b, InputIter e, std::false_type);
+
 template <class FwdIterator>
 iterator insertImpl(
+      const_iterator i,
+      FwdIterator s1,
+      FwdIterator s2,
+      std::forward_iterator_tag);
+
 template <class InputIterator>
 iterator insertImpl(
+      const_iterator i,
+      InputIterator b,
+      InputIterator e,
+      std::input_iterator_tag);
+
+ public:
   template <class ItOrLength, class ItOrChar>
   iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
     using Sel = std::integral_constant<
@@ -1540,36 +1604,37 @@ public:
     return replace(i1, i2, s, traitsLength(s));
   }
 
-private:
- basic_fbstring& replaceImplDiscr(
-     iterator i1,
-     iterator i2,
-     const value_type* s,
-     size_type n,
-     std::integral_constant<int, 2>);
-
- basic_fbstring& replaceImplDiscr(
-     iterator i1,
-     iterator i2,
-     size_type n2,
-     value_type c,
-     std::integral_constant<int, 1>);
-
- template <class InputIter>
- basic_fbstring& replaceImplDiscr(
-     iterator i1,
-     iterator i2,
-     InputIter b,
-     InputIter e,
-     std::integral_constant<int, 0>);
-
-private:
- template <class FwdIterator>
- bool replaceAliased(iterator /* i1 */,
-                     iterator /* i2 */,
-                     FwdIterator /* s1 */,
-                     FwdIterator /* s2 */,
-                     std::false_type) {
+ private:
+  basic_fbstring& replaceImplDiscr(
+      iterator i1,
+      iterator i2,
+      const value_type* s,
+      size_type n,
+      std::integral_constant<int, 2>);
+
+  basic_fbstring& replaceImplDiscr(
+      iterator i1,
+      iterator i2,
+      size_type n2,
+      value_type c,
+      std::integral_constant<int, 1>);
+
+  template <class InputIter>
+  basic_fbstring& replaceImplDiscr(
+      iterator i1,
+      iterator i2,
+      InputIter b,
+      InputIter e,
+      std::integral_constant<int, 0>);
+
+ private:
+  template <class FwdIterator>
+  bool replaceAliased(
+      iterator /* i1 */,
+      iterator /* i2 */,
+      FwdIterator /* s1 */,
+      FwdIterator /* s2 */,
+      std::false_type) {
     return false;
   }
 
@@ -1781,7 +1846,7 @@ private:
     return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
   }
 
-private:
+ private:
   // Data
   Storage store_;
 };
@@ -1824,8 +1889,13 @@ inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(
 }
 
 template <typename E, class T, class A, class S>
-inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(
-    const value_type c) {
+template <typename TP>
+inline typename std::enable_if<
+    std::is_same<
+        typename std::decay<TP>::type,
+        typename basic_fbstring<E, T, A, S>::value_type>::value,
+    basic_fbstring<E, T, A, S>&>::type
+basic_fbstring<E, T, A, S>::operator=(TP c) {
   Invariant checker(*this);
 
   if (empty()) {
@@ -2263,7 +2333,7 @@ basic_fbstring<E, T, A, S>::find_first_of(
   }
   const_iterator i(begin() + pos), finish(end());
   for (; i != finish; ++i) {
-    if (traits_type::find(s, n, *i) != 0) {
+    if (traits_type::find(s, n, *i) != nullptr) {
       return i - begin();
     }
   }
@@ -2278,7 +2348,7 @@ basic_fbstring<E, T, A, S>::find_last_of(
     pos = std::min(pos, length() - 1);
     const_iterator i(begin() + pos);
     for (;; --i) {
-      if (traits_type::find(s, n, *i) != 0) {
+      if (traits_type::find(s, n, *i) != nullptr) {
         return i - begin();
       }
       if (i == begin()) {
@@ -2296,7 +2366,7 @@ basic_fbstring<E, T, A, S>::find_first_not_of(
   if (pos < length()) {
     const_iterator i(begin() + pos), finish(end());
     for (; i != finish; ++i) {
-      if (traits_type::find(s, n, *i) == 0) {
+      if (traits_type::find(s, n, *i) == nullptr) {
         return i - begin();
       }
     }
@@ -2312,7 +2382,7 @@ basic_fbstring<E, T, A, S>::find_last_not_of(
     pos = std::min(pos, size() - 1);
     const_iterator i(begin() + pos);
     for (;; --i) {
-      if (traits_type::find(s, n, *i) == 0) {
+      if (traits_type::find(s, n, *i) == nullptr) {
         return i - begin();
       }
       if (i == begin()) {
@@ -2607,13 +2677,13 @@ std::basic_istream<
     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;
+  typedef std::basic_istream<
+      typename basic_fbstring<E, T, A, S>::value_type,
+      typename basic_fbstring<E, T, A, S>::traits_type>
+      _istream_type;
+  typename _istream_type::sentry sentry(is);
   size_t extracted = 0;
-  auto err = __ios_base::goodbit;
+  auto err = _istream_type::goodbit;
   if (sentry) {
     auto n = is.width();
     if (n <= 0) {
@@ -2622,7 +2692,7 @@ std::basic_istream<
     str.erase();
     for (auto got = is.rdbuf()->sgetc(); extracted != size_t(n); ++extracted) {
       if (got == T::eof()) {
-        err |= __ios_base::eofbit;
+        err |= _istream_type::eofbit;
         is.width(0);
         break;
       }
@@ -2634,7 +2704,7 @@ std::basic_istream<
     }
   }
   if (!extracted) {
-    err |= __ios_base::failbit;
+    err |= _istream_type::failbit;
   }
   if (err) {
     is.setstate(err);
@@ -2651,28 +2721,31 @@ operator<<(
   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::basic_ostream<
+      typename basic_fbstring<E, T, A, S>::value_type,
+      typename basic_fbstring<E, T, A, S>::traits_type>
+      _ostream_type;
+  typename _ostream_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;
+        (os.flags() & _ostream_type::adjustfield) == _ostream_type::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);
+      os.setstate(_ostream_type::badbit | _ostream_type::failbit);
     }
   }
 #elif defined(_MSC_VER)
+  typedef decltype(os.precision()) streamsize;
   // MSVC doesn't define __ostream_insert
-  os.write(str.data(), str.size());
+  os.write(str.data(), static_cast<streamsize>(str.size()));
 #else
   std::__ostream_insert(os, str.data(), str.size());
 #endif
@@ -2686,34 +2759,90 @@ constexpr typename basic_fbstring<E1, T, A, S>::size_type
 #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) {
+template <typename E, class T, class A, class S, class A2>
+inline bool operator==(
+    const basic_fbstring<E, T, A, S>& lhs,
+    const std::basic_string<E, T, A2>& 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) {
+template <typename E, class T, class A, class S, class A2>
+inline bool operator==(
+    const std::basic_string<E, T, A2>& 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) {
+template <typename E, class T, class A, class S, class A2>
+inline bool operator!=(
+    const basic_fbstring<E, T, A, S>& lhs,
+    const std::basic_string<E, T, A2>& 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) {
+template <typename E, class T, class A, class S, class A2>
+inline bool operator!=(
+    const std::basic_string<E, T, A2>& lhs,
+    const basic_fbstring<E, T, A, S>& rhs) {
   return !(lhs == rhs);
 }
 
+template <typename E, class T, class A, class S, class A2>
+inline bool operator<(
+    const basic_fbstring<E, T, A, S>& lhs,
+    const std::basic_string<E, T, A2>& rhs) {
+  return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) < 0;
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator>(
+    const basic_fbstring<E, T, A, S>& lhs,
+    const std::basic_string<E, T, A2>& rhs) {
+  return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) > 0;
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator<(
+    const std::basic_string<E, T, A2>& lhs,
+    const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs > lhs;
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator>(
+    const std::basic_string<E, T, A2>& lhs,
+    const basic_fbstring<E, T, A, S>& rhs) {
+  return rhs < lhs;
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator<=(
+    const basic_fbstring<E, T, A, S>& lhs,
+    const std::basic_string<E, T, A2>& rhs) {
+  return !(lhs > rhs);
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator>=(
+    const basic_fbstring<E, T, A, S>& lhs,
+    const std::basic_string<E, T, A2>& rhs) {
+  return !(lhs < rhs);
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator<=(
+    const std::basic_string<E, T, A2>& lhs,
+    const basic_fbstring<E, T, A, S>& rhs) {
+  return !(lhs > rhs);
+}
+
+template <typename E, class T, class A, class S, class A2>
+inline bool operator>=(
+    const std::basic_string<E, T, A2>& lhs,
+    const basic_fbstring<E, T, A, S>& rhs) {
+  return !(lhs < rhs);
+}
+
 #if !defined(_LIBSTDCXX_FBSTRING)
 typedef basic_fbstring<char> fbstring;
 #endif
@@ -2722,11 +2851,9 @@ typedef basic_fbstring<char> fbstring;
 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
+FOLLY_FBSTRING_END_NAMESPACE
 
 #ifndef _LIBSTDCXX_FBSTRING
 
@@ -2754,24 +2881,14 @@ 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
+} // namespace std
 
 #undef FOLLY_FBSTRING_HASH
 #undef FOLLY_FBSTRING_HASH1
 
 #endif // _LIBSTDCXX_FBSTRING
 
-#pragma GCC diagnostic pop
+FOLLY_POP_WARNING
 
 #undef FBSTRING_DISABLE_SSO
 #undef FBSTRING_SANITIZE_ADDRESS
@@ -2779,3 +2896,13 @@ FOLLY_FBSTRING_HASH
 #undef FBSTRING_LIKELY
 #undef FBSTRING_UNLIKELY
 #undef FBSTRING_ASSERT
+
+#ifndef _LIBSTDCXX_FBSTRING
+namespace folly {
+template <class T>
+struct IsSomeString;
+
+template <>
+struct IsSomeString<fbstring> : std::true_type {};
+} // namespace folly
+#endif