folly/folly/FBString.h

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// String type.

#pragma once

#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <iosfwd>
#include <limits>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>

#include <fmt/format.h>
#include <folly/CPortability.h>
#include <folly/CppAttributes.h>
#include <folly/Likely.h>
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/hash/Hash.h>
#include <folly/lang/Assume.h>
#include <folly/lang/CheckedMath.h>
#include <folly/lang/Exception.h>
#include <folly/memory/Malloc.h>

#if FOLLY_CPLUSPLUS >= 202002L
#include <compare>
#endif

FOLLY_PUSH_WARNING
// Ignore shadowing warnings within this file, so includers can use -Wshadow.
FOLLY_GNU_DISABLE_WARNING()

namespace folly {

// When compiling with ASan, always heap-allocate the string even if
// it would fit in-situ, so that ASan can detect access to the string
// buffer after it has been invalidated (destroyed, resized, etc.).
// Note that this flag doesn't remove support for in-situ strings, as
// that would break ABI-compatibility and wouldn't allow linking code
// compiled with this flag with code compiled without.
#ifdef FOLLY_SANITIZE_ADDRESS
#define FBSTRING_DISABLE_SSO
#else
#define FBSTRING_DISABLE_SSO
#endif

namespace fbstring_detail {

template <class InIt, class OutIt>
inline std::pair<InIt, OutIt> copy_n(
    InIt b, typename std::iterator_traits<InIt>::difference_type n, OutIt d) {}

template <class Pod, class T>
inline void podFill(Pod* b, Pod* e, T 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 podCopy(const Pod* b, const Pod* e, Pod* d) {}

/*
 * Lightly structured memmove, simplifies copying PODs and introduces
 * some asserts
 */
template <class Pod>
inline void podMove(const Pod* b, const Pod* e, Pod* d) {}
} // 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& operator=(const fbstring_core_model &) = delete;
  ~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* mutableData();
  // 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. The expanded region is
  // zero-terminated. 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.
  // If expGrowth is true, exponential growth is guaranteed.
  // It is not guaranteed not to reallocate even if size() + delta <
  // capacity(), so all references to the buffer are invalidated.
  Char* expandNoinit(size_t delta, bool expGrowth);
  // 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);
};
*/

/**
 * This is the core of the string. The code should work on 32- and
 * 64-bit and both big- and little-endianan architectures with any
 * Char size.
 *
 * 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 two
 * bits of the rightmost char of the storage:
 * - If neither is set, then the string is small. Its length is represented by
 *   the lower-order bits on little-endian or the high-order bits on big-endian
 *   of that rightmost character. The value of these six bits is
 *   `maxSmallSize - size`, so this quantity must be subtracted from
 *   `maxSmallSize` to compute the `size` of the string (see `smallSize()`).
 *   This scheme ensures that when `size == `maxSmallSize`, the last byte in the
 *   storage is \0. This way, storage will be a null-terminated sequence of
 *   bytes, even if all 23 bytes of data are used on a 64-bit architecture.
 *   This enables `c_str()` and `data()` to simply return a pointer to the
 *   storage.
 *
 * - If the MSb is set, the string is medium width.
 *
 * - If the second MSb is set, then the string is large. On little-endian,
 *   these 2 bits are the 2 MSbs of MediumLarge::capacity_, while on
 *   big-endian, these 2 bits are the 2 LSbs. This keeps both little-endian
 *   and big-endian fbstring_core equivalent with merely different ops used
 *   to extract capacity/category.
 */
template <class Char>
class fbstring_core {};

template <class Char>
inline void fbstring_core<Char>::copySmall(const fbstring_core& rhs) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::copyMedium(const fbstring_core& rhs) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::copyLarge(const fbstring_core& rhs) {}

// Small strings are bitblitted
template <class Char>
inline void fbstring_core<Char>::initSmall(
    const Char* const data, const size_t size) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::initMedium(
    const Char* const data, const size_t size) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::initLarge(
    const Char* const data, const size_t size) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::unshare(size_t minCapacity) {}

template <class Char>
inline Char* fbstring_core<Char>::mutableDataLarge() {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::reserveLarge(size_t minCapacity) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::reserveMedium(
    const size_t minCapacity) {}

template <class Char>
FOLLY_NOINLINE void fbstring_core<Char>::reserveSmall(
    size_t minCapacity, const bool disableSSO) {}

template <class Char>
inline Char* fbstring_core<Char>::expandNoinit(
    const size_t delta,
    bool expGrowth, /* = false */
    bool disableSSO /* = FBSTRING_DISABLE_SSO */) {}

template <class Char>
inline void fbstring_core<Char>::shrinkSmall(const size_t delta) {}

template <class Char>
inline void fbstring_core<Char>::shrinkMedium(const size_t delta) {}

template <class Char>
inline void fbstring_core<Char>::shrinkLarge(const size_t delta) {}

/**
 * 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 {};

/**
 * This is the basic_string replacement. For conformity,
 * basic_fbstring takes the same template parameters, plus the last
 * one which is the core.
 */
template <
    typename E,
    class T = std::char_traits<E>,
    class A = std::allocator<E>,
    class Storage = fbstring_core<E>>
class basic_fbstring {
  static_assert(
      std::is_same<A, std::allocator<E>>::value,
      "fbstring ignores custom allocators");

  template <typename Ex, typename... Args>
  FOLLY_ALWAYS_INLINE static void enforce(bool condition, Args&&... args) {}

  bool isSane() const {}

  struct Invariant {
    Invariant& operator=(const Invariant&) = delete;
    explicit Invariant(const basic_fbstring& s) noexcept : s_(s) {
      assert(s_.isSane());
    }
    ~Invariant() noexcept { assert(s_.isSane()); }

   private:
    const basic_fbstring& s_;
  };

 public:
  // types
  typedef T traits_type;
  typedef typename traits_type::char_type value_type;
  typedef A allocator_type;
  typedef typename std::allocator_traits<A>::size_type size_type;
  typedef typename std::allocator_traits<A>::difference_type difference_type;

  typedef typename std::allocator_traits<A>::value_type& reference;
  typedef typename std::allocator_traits<A>::value_type const& const_reference;
  typedef typename std::allocator_traits<A>::pointer pointer;
  typedef typename std::allocator_traits<A>::const_pointer const_pointer;

  typedef E* iterator;
  typedef const E* const_iterator;
  typedef std::reverse_iterator<iterator> reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

  static constexpr size_type npos = size_type(-1);
  typedef std::true_type IsRelocatable;

 private:
  using string_view_type = std::basic_string_view<value_type, traits_type>;

  static void procrustes(size_type& n, size_type nmax) {}

  static size_type traitsLength(const value_type* s);

  struct string_view_ctor {};
  FOLLY_NOINLINE basic_fbstring(
      string_view_type view, const A&, string_view_ctor)
      :{}

 public:
  // C++11 21.4.2 construct/copy/destroy

  // Note: while the following two constructors can be (and previously were)
  // collapsed into one constructor written this way:
  //
  //   explicit basic_fbstring(const A& a = A()) noexcept { }
  //
  // This can cause Clang (at least version 3.7) to fail with the error:
  //   "chosen constructor is explicit in copy-initialization ...
  //   in implicit initialization of field '(x)' with omitted initializer"
  //
  // if used in a struct which is default-initialized.  Hence the split into
  // these two separate constructors.

  basic_fbstring() noexcept :{}
  /* implicit */ basic_fbstring(std::nullptr_t) = delete;

  explicit basic_fbstring(const A&) noexcept {}

  basic_fbstring(const basic_fbstring& str) :{}

  // Move constructor
  basic_fbstring(basic_fbstring&& goner) noexcept
      :{}

  // This is defined for compatibility with std::string
  template <typename A2>
  /* implicit */ basic_fbstring(const std::basic_string<E, T, A2>& str)
      : store_(str.data(), str.size()) {}

  basic_fbstring(
      const basic_fbstring& str,
      size_type pos,
      size_type n = npos,
      const A& /* a */ = A()) {}

  FOLLY_NOINLINE
  /* implicit */ basic_fbstring(const value_type* s, const A& /*a*/ = A())
      :{}

  FOLLY_NOINLINE
  basic_fbstring(const value_type* s, size_type n, const A& /*a*/ = A())
      :{}

  FOLLY_NOINLINE
  basic_fbstring(size_type n, value_type c, const A& /*a*/ = A()) {}

  template <class InIt>
  FOLLY_NOINLINE basic_fbstring(
      InIt begin,
      InIt end,
      typename std::enable_if<
          !std::is_same<InIt, value_type*>::value,
          const A>::type& /*a*/
      = A()) {}

  // Specialization for const char*, const char*
  FOLLY_NOINLINE
  basic_fbstring(const value_type* b, const value_type* e, const A& /*a*/ = A())
      :{}

  // Nonstandard constructor
  basic_fbstring(
      value_type* s, size_type n, size_type c, AcquireMallocatedString a)
      :{}

  // Construction from initialization list
  FOLLY_NOINLINE
  basic_fbstring(std::initializer_list<value_type> il) {}

  template <
      typename StringViewLike,
      std::enable_if_t<
          std::is_convertible_v<const StringViewLike&, string_view_type> &&
              !std::is_convertible_v<const StringViewLike&, const value_type*>,
          int> = 0>
  explicit basic_fbstring(const StringViewLike& view, const A& a = A())
      : basic_fbstring(string_view_type(view), a, string_view_ctor{}

  template <
      typename StringViewLike,
      std::enable_if_t<
          std::is_convertible_v<const StringViewLike&, string_view_type> &&
              !std::is_convertible_v<const StringViewLike&, const value_type*>,
          int> = 0>
  basic_fbstring(
      const StringViewLike& view, size_type pos, size_type n, const A& a = A())
      : basic_fbstring(
            string_view_type(view).substr(pos, n), a, string_view_ctor{}

  ~basic_fbstring() noexcept {}

  basic_fbstring& operator=(const basic_fbstring& lhs);

  // Move assignment
  basic_fbstring& operator=(basic_fbstring&& goner) noexcept;

  // Compatibility with std::string
  template <typename A2>
  basic_fbstring& operator=(const std::basic_string<E, T, A2>& rhs) {}

  // Compatibility with std::string
  std::basic_string<E, T, A> toStdString() const {}

  basic_fbstring& operator=(std::nullptr_t) = delete;

  basic_fbstring& operator=(const value_type* 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.
  template <typename TP>
  typename std::enable_if<
      std::is_convertible<
          TP,
          typename basic_fbstring<E, T, A, Storage>::value_type>::value &&
          !std::is_same<
              typename std::decay<TP>::type,
              typename basic_fbstring<E, T, A, Storage>::value_type>::value,
      basic_fbstring<E, T, A, Storage>&>::type
  operator=(TP c) = delete;

  basic_fbstring& operator=(std::initializer_list<value_type> il) {}

  operator string_view_type() const noexcept { return {data(), size()}; }

  // C++11 21.4.3 iterators:
  iterator begin() {}

  const_iterator begin() const {}

  const_iterator cbegin() const {}

  iterator end() {}

  const_iterator end() const {}

  const_iterator cend() const {}

  reverse_iterator rbegin() {}

  const_reverse_iterator rbegin() const {}

  const_reverse_iterator crbegin() const {}

  reverse_iterator rend() {}

  const_reverse_iterator rend() const {}

  const_reverse_iterator crend() const {}

  // Added by C++11
  // C++11 21.4.5, element access:
  const value_type& front() const {}
  const value_type& back() const {}
  value_type& front() {}
  value_type& back() {}
  void pop_back() {}

  // C++11 21.4.4 capacity:
  size_type size() const {}

  size_type length() const {}

  size_type max_size() const {}

  void resize(size_type n, value_type c = value_type());

  size_type capacity() const {}

  void reserve(size_type res_arg = 0) {}

  void shrink_to_fit() {}

  void clear() {}

  bool empty() const {}

  // C++11 21.4.5 element access:
  const_reference operator[](size_type pos) const {}

  reference operator[](size_type pos) {}

  const_reference at(size_type n) const {}

  reference at(size_type n) {}

  // C++11 21.4.6 modifiers:
  basic_fbstring& operator+=(const basic_fbstring& str) {}

  basic_fbstring& operator+=(const value_type* s) {}

  basic_fbstring& operator+=(const value_type c) {}

  basic_fbstring& operator+=(std::initializer_list<value_type> il) {}

  basic_fbstring& append(const basic_fbstring& str);

  basic_fbstring& append(
      const basic_fbstring& str, const size_type pos, size_type n);

  basic_fbstring& append(const value_type* s, size_type n);

  basic_fbstring& append(const value_type* s) {}

  basic_fbstring& append(size_type n, value_type c);

  template <class InputIterator>
  basic_fbstring& append(InputIterator first, InputIterator last) {}

  basic_fbstring& append(std::initializer_list<value_type> il) {}

  void push_back(const value_type c) {}

  basic_fbstring& assign(const basic_fbstring& str) {}

  basic_fbstring& assign(basic_fbstring&& str) {}

  basic_fbstring& assign(
      const basic_fbstring& str, const size_type pos, size_type n);

  basic_fbstring& assign(const value_type* s, const size_type n);

  basic_fbstring& assign(const value_type* s) {}

  basic_fbstring& assign(std::initializer_list<value_type> il) {}

  template <class ItOrLength, class ItOrChar>
  basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {}

  basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {}

  basic_fbstring& insert(
      size_type pos1, const basic_fbstring& str, size_type pos2, size_type n) {}

  basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {}

  basic_fbstring& insert(size_type pos, const value_type* s) {}

  basic_fbstring& insert(size_type pos, size_type n, value_type c) {}

  iterator insert(const_iterator p, const value_type c) {}

 private:
  typedef std::basic_istream<value_type, traits_type> istream_type;
  istream_type& getlineImpl(istream_type& is, value_type delim);

 public:
  friend inline istream_type& getline(
      istream_type& is, basic_fbstring& str, value_type delim) {
    return str.getlineImpl(is, delim);
  }

  friend inline istream_type& getline(istream_type& is, basic_fbstring& str) {
    return getline(is, str, '\n');
  }

 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) {}

  iterator insert(const_iterator p, std::initializer_list<value_type> il) {}

  basic_fbstring& erase(size_type pos = 0, size_type n = npos) {}

  iterator erase(iterator position) {}

  iterator erase(iterator first, iterator last) {}

  // 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) {}

  // 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) {}

  // 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) {}

  // 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) {}

  basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {}

  basic_fbstring& replace(iterator i1, iterator i2, const value_type* 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) {}

  template <class FwdIterator>
  bool replaceAliased(
      iterator i1, iterator i2, FwdIterator s1, FwdIterator s2, std::true_type);

  template <class FwdIterator>
  void replaceImpl(
      iterator i1,
      iterator i2,
      FwdIterator s1,
      FwdIterator s2,
      std::forward_iterator_tag);

  template <class InputIterator>
  void replaceImpl(
      iterator i1,
      iterator i2,
      InputIterator b,
      InputIterator e,
      std::input_iterator_tag);

 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) {}

  size_type copy(value_type* s, size_type n, size_type pos = 0) const {}

  void swap(basic_fbstring& rhs) {}

  const value_type* c_str() const {}

  const value_type* data() const {}

  value_type* data() {}

  allocator_type get_allocator() const {}

  size_type find(const basic_fbstring& str, size_type pos = 0) const {}

  size_type find(
      const value_type* needle, size_type pos, size_type nsize) const;

  size_type find(const value_type* s, size_type pos = 0) const {}

  size_type find(value_type c, size_type pos = 0) const {}

  size_type rfind(const basic_fbstring& str, size_type pos = npos) const {}

  size_type rfind(const value_type* s, size_type pos, size_type n) const;

  size_type rfind(const value_type* s, size_type pos = npos) const {}

  size_type rfind(value_type c, size_type pos = npos) const {}

  size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {}

  size_type find_first_of(
      const value_type* s, size_type pos, size_type n) const;

  size_type find_first_of(const value_type* s, size_type pos = 0) const {}

  size_type find_first_of(value_type c, size_type pos = 0) const {}

  size_type find_last_of(
      const basic_fbstring& str, size_type pos = npos) const {}

  size_type find_last_of(const value_type* s, size_type pos, size_type n) const;

  size_type find_last_of(const value_type* s, size_type pos = npos) const {}

  size_type find_last_of(value_type c, size_type pos = npos) const {}

  size_type find_first_not_of(
      const basic_fbstring& str, size_type pos = 0) const {}

  size_type find_first_not_of(
      const value_type* s, size_type pos, size_type n) const;

  size_type find_first_not_of(const value_type* s, size_type pos = 0) const {}

  size_type find_first_not_of(value_type c, size_type pos = 0) const {}

  size_type find_last_not_of(
      const basic_fbstring& str, size_type pos = npos) const {}

  size_type find_last_not_of(
      const value_type* s, size_type pos, size_type n) const;

  size_type find_last_not_of(const value_type* s, size_type pos = npos) const {}

  size_type find_last_not_of(value_type c, size_type pos = npos) const {}

  basic_fbstring substr(size_type pos = 0, size_type n = npos) const& {}

  basic_fbstring substr(size_type pos = 0, size_type n = npos) && {}

  int compare(const basic_fbstring& str) const {}

  int compare(size_type pos1, size_type n1, const basic_fbstring& str) const {}

  int compare(size_type pos1, size_type n1, const value_type* s) const {}

  int compare(
      size_type pos1, size_type n1, const value_type* s, size_type n2) const {}

  int compare(
      size_type pos1,
      size_type n1,
      const basic_fbstring& str,
      size_type pos2,
      size_type n2) const {}

  // Code from Jean-Francois Bastien (03/26/2007)
  int compare(const value_type* s) const {}

#if FOLLY_CPLUSPLUS >= 202002L
  friend auto operator<=>(
      const basic_fbstring& lhs, const basic_fbstring& rhs) {
    return lhs.spaceship(rhs.data(), rhs.size());
  }
  friend auto operator<=>(const basic_fbstring& lhs, const char* rhs) {
    return lhs.spaceship(rhs, traitsLength(rhs));
  }
  template <typename A2>
  friend auto operator<=>(
      const basic_fbstring& lhs, const std::basic_string<E, T, A2>& rhs) {
    return lhs.spaceship(rhs.data(), rhs.size());
  }
#endif // FOLLY_CPLUSPLUS >= 202002L

 private:
#if FOLLY_CPLUSPLUS >= 202002L
  auto spaceship(const value_type* rhsData, size_type rhsSize) const {
    auto c = compare(0, size(), rhsData, rhsSize);
    if (c == 0) {
      return std::strong_ordering::equal;
    } else if (c < 0) {
      return std::strong_ordering::less;
    } else {
      return std::strong_ordering::greater;
    }
  }
#endif // FOLLY_CPLUSPLUS >= 202002L

  // Data
  Storage store_;
};

template <typename E, class T, class A, class S>
FOLLY_NOINLINE typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::traitsLength(const value_type* s) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(
    const basic_fbstring& lhs) {}

// Move assignment
template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(
    basic_fbstring&& goner) noexcept {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(
    value_type c) {}

template <typename E, class T, class A, class S>
inline void basic_fbstring<E, T, A, S>::resize(
    const size_type n, const value_type c /*= value_type()*/) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(
    const basic_fbstring& str) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(
    const basic_fbstring& str, const size_type pos, size_type n) {}

template <typename E, class T, class A, class S>
FOLLY_NOINLINE basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(
    const value_type* s, size_type n) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(
    size_type n, value_type c) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::assign(
    const basic_fbstring& str, const size_type pos, size_type n) {}

template <typename E, class T, class A, class S>
FOLLY_NOINLINE basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::assign(
    const value_type* s, const size_type n) {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::istream_type&
basic_fbstring<E, T, A, S>::getlineImpl(istream_type& is, value_type delim) {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::find(
    const value_type* needle,
    const size_type pos,
    const size_type nsize) const {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::iterator
basic_fbstring<E, T, A, S>::insertImplDiscr(
    const_iterator i, size_type n, value_type c, std::true_type) {}

template <typename E, class T, class A, class S>
template <class InputIter>
inline typename basic_fbstring<E, T, A, S>::iterator
basic_fbstring<E, T, A, S>::insertImplDiscr(
    const_iterator i, InputIter b, InputIter e, std::false_type) {}

template <typename E, class T, class A, class S>
template <class FwdIterator>
inline typename basic_fbstring<E, T, A, S>::iterator
basic_fbstring<E, T, A, S>::insertImpl(
    const_iterator i,
    FwdIterator s1,
    FwdIterator s2,
    std::forward_iterator_tag) {}

template <typename E, class T, class A, class S>
template <class InputIterator>
inline typename basic_fbstring<E, T, A, S>::iterator
basic_fbstring<E, T, A, S>::insertImpl(
    const_iterator i,
    InputIterator b,
    InputIterator e,
    std::input_iterator_tag) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr(
    iterator i1,
    iterator i2,
    const value_type* s,
    size_type n,
    std::integral_constant<int, 2>) {}

template <typename E, class T, class A, class S>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr(
    iterator i1,
    iterator i2,
    size_type n2,
    value_type c,
    std::integral_constant<int, 1>) {}

template <typename E, class T, class A, class S>
template <class InputIter>
inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr(
    iterator i1,
    iterator i2,
    InputIter b,
    InputIter e,
    std::integral_constant<int, 0>) {}

template <typename E, class T, class A, class S>
template <class FwdIterator>
inline bool basic_fbstring<E, T, A, S>::replaceAliased(
    iterator i1, iterator i2, FwdIterator s1, FwdIterator s2, std::true_type) {}

template <typename E, class T, class A, class S>
template <class FwdIterator>
inline void basic_fbstring<E, T, A, S>::replaceImpl(
    iterator i1,
    iterator i2,
    FwdIterator s1,
    FwdIterator s2,
    std::forward_iterator_tag) {}

template <typename E, class T, class A, class S>
template <class InputIterator>
inline void basic_fbstring<E, T, A, S>::replaceImpl(
    iterator i1,
    iterator i2,
    InputIterator b,
    InputIterator e,
    std::input_iterator_tag) {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::rfind(
    const value_type* s, size_type pos, size_type n) const {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::find_first_of(
    const value_type* s, size_type pos, size_type n) const {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::find_last_of(
    const value_type* s, size_type pos, size_type n) const {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::find_first_not_of(
    const value_type* s, size_type pos, size_type n) const {}

template <typename E, class T, class A, class S>
inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::find_last_not_of(
    const value_type* s, size_type pos, size_type n) const {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

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) {}

template <typename E, class T, class A, class S>
inline bool operator==(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

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) {}

template <typename E, class T, class A, class S>
inline bool operator==(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;

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) {}

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) {}

template <typename E, class T, class A, class S>
inline bool operator!=(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

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) {}

template <typename E, class T, class A, class S>
inline bool operator!=(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;

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) {}

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) {}

template <typename E, class T, class A, class S>
inline bool operator<(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;

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) {}

template <typename E, class T, class A, class S>
inline bool operator<(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

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) {}

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) {}

template <typename E, class T, class A, class S>
inline bool operator>(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;

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) {}

template <typename E, class T, class A, class S>
inline bool operator>(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

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) {}

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) {}

template <typename E, class T, class A, class S>
inline bool operator<=(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;

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) {}

template <typename E, class T, class A, class S>
inline bool operator<=(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

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) {}

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) {}

template <typename E, class T, class A, class S>
inline bool operator>=(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;

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) {}

template <typename E, class T, class A, class S>
inline bool operator>=(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

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) {}

#if FOLLY_CPLUSPLUS >= 202002
template <typename E, class T, class A, class S>
inline bool operator<=>(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =
    delete;

template <typename E, class T, class A, class S>
inline bool operator<=>(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =
    delete;
#endif

// 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) {}

// 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) {}

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) {}

// basic_string compatibility routines

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

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) {}

fbstring;

// fbstring is relocatable
IsRelocatable<basic_fbstring<T, R, A, S>>;

// 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
inline std::string toStdString(const folly::fbstring& s) {}

inline const std::string& toStdString(const std::string& 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) {}

} // namespace folly

// Hash functions to make fbstring usable with e.g. unordered_map

#define FOLLY_FBSTRING_HASH1

// The C++11 standard says that these four are defined for basic_string
#define FOLLY_FBSTRING_HASH

namespace std {

FOLLY_FBSTRING_HASH

} // namespace std

#undef FOLLY_FBSTRING_HASH
#undef FOLLY_FBSTRING_HASH1

FOLLY_POP_WARNING

#undef FBSTRING_DISABLE_SSO

namespace folly {
template <class T>
struct IsSomeString;

template <>
struct IsSomeString<fbstring> : std::true_type {};
} // namespace folly

template <>
struct fmt::formatter<folly::fbstring> : private formatter<fmt::string_view> {};