X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FFBString.h;h=4882aac577650dc0689c91baa9269e5bf4438390;hp=64249c2845408f390935280ade400a49405cbb43;hb=6ae2206d4a23d683877e33919a469abfdfc411e5;hpb=427c43f4cb461f375b8598ebc55766fd677963c4 diff --git a/folly/FBString.h b/folly/FBString.h index 64249c28..4882aac5 100644 --- a/folly/FBString.h +++ b/folly/FBString.h @@ -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 +#include +#include #include #include @@ -31,13 +33,14 @@ #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 #include // libc++ doesn't provide this header, nor does msvc @@ -51,16 +54,10 @@ #include #include -#include -#include #include - -#if FOLLY_HAVE_DEPRECATED_ASSOC -#ifdef _GLIBCXX_SYMVER -#include -#include -#endif -#endif +#include +#include +#include // When used in folly, assertions are not disabled. #define FBSTRING_ASSERT(expr) assert(expr) @@ -77,13 +74,13 @@ #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_* @@ -91,12 +88,19 @@ #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 copy_n( template 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) { @@ -166,6 +171,9 @@ inline void podFill(Pod* b, Pod* e, T c) { */ template inline void podCopy(const Pod* b, const Pod* e, Pod* d) { + FBSTRING_ASSERT(b != nullptr); + FBSTRING_ASSERT(e != nullptr); + FBSTRING_ASSERT(d != nullptr); FBSTRING_ASSERT(e >= b); FBSTRING_ASSERT(d >= e || d + (e - b) <= b); memcpy(d, b, (e - b) * sizeof(Pod)); @@ -223,7 +231,7 @@ enum class AcquireMallocatedString {}; template class fbstring_core_model { -public: + public: fbstring_core_model(); fbstring_core_model(const fbstring_core_model &); ~fbstring_core_model(); @@ -272,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 &); }; @@ -286,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 @@ -306,7 +313,7 @@ private: * to extract capacity/category. */ template 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 @@ -318,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); @@ -344,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( @@ -484,7 +501,9 @@ public: if (RefCounted::refs(ml_.data_) > 1) { return ml_.size_; } - default: {} + break; + default: + break; } return ml_.capacity(); } @@ -493,7 +512,7 @@ public: return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1; } -private: + private: // Disabled fbstring_core & operator=(const fbstring_core & rhs); @@ -515,11 +534,14 @@ private: std::atomic refCount_; Char data_[1]; + constexpr static size_t getDataOffset() { + return offsetof(RefCounted, data_); + } + static RefCounted * fromData(Char * p) { - return static_cast( - static_cast( - static_cast(static_cast(p)) - - sizeof(refCount_))); + return static_cast(static_cast( + static_cast(static_cast(p)) - + getDataOffset())); } static size_t refs(Char * p) { @@ -540,40 +562,39 @@ 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(checkedMalloc(allocSize)); result->refCount_.store(1, std::memory_order_release); - *size = (allocSize - sizeof(RefCounted)) / sizeof(Char); + *size = (allocSize - getDataOffset()) / sizeof(Char) - 1; return result; } static RefCounted * create(const Char * data, size_t * size) { const size_t effectiveSize = *size; auto result = create(size); - fbstring_detail::podCopy(data, data + effectiveSize, result->data_); + if (FBSTRING_LIKELY(effectiveSize > 0)) { + fbstring_detail::podCopy(data, data + effectiveSize, result->data_); + } return result; } static RefCounted * reallocate(Char *const data, const size_t currentSize, const size_t currentCapacity, - const size_t newCapacity) { - 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( - smartRealloc(dis, - sizeof(RefCounted) + currentSize * sizeof(Char), - sizeof(RefCounted) + currentCapacity * sizeof(Char), - sizeof(RefCounted) + newCapacity * sizeof(Char))); + auto result = static_cast(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; } }; @@ -603,7 +624,6 @@ private: } void setCapacity(size_t cap, Category cat) { - FBSTRING_ASSERT(cat != Category::isLarge); capacity_ = kIsLittleEndian ? cap | (static_cast(cat) << kCategoryShift) : (cap << 2) | static_cast(cat); @@ -642,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); @@ -668,7 +688,7 @@ private: }; template -inline void fbstring_core::makeSmall(const fbstring_core& rhs) { +inline void fbstring_core::copySmall(const fbstring_core& rhs) { static_assert(offsetof(MediumLarge, data_) == 0, "fbstring layout failure"); static_assert( offsetof(MediumLarge, size_) == sizeof(ml_.data_), @@ -687,7 +707,7 @@ inline void fbstring_core::makeSmall(const fbstring_core& rhs) { } template -FOLLY_MALLOC_NOINLINE inline void fbstring_core::makeMedium( +FOLLY_MALLOC_NOINLINE inline void fbstring_core::copyMedium( const fbstring_core& rhs) { // Medium strings are copied eagerly. Don't forget to allocate // one extra Char for the null terminator. @@ -702,7 +722,7 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core::makeMedium( } template -FOLLY_MALLOC_NOINLINE inline void fbstring_core::makeLarge( +FOLLY_MALLOC_NOINLINE inline void fbstring_core::copyLarge( const fbstring_core& rhs) { // Large strings are just refcounted ml_ = rhs.ml_; @@ -737,10 +757,13 @@ inline void fbstring_core::initSmall( switch ((byteSize + wordWidth - 1) / wordWidth) { // Number of words. case 3: ml_.capacity_ = reinterpret_cast(data)[2]; + FOLLY_FALLTHROUGH; case 2: ml_.size_ = reinterpret_cast(data)[1]; + FOLLY_FALLTHROUGH; case 1: ml_.data_ = *reinterpret_cast(const_cast(data)); + FOLLY_FALLTHROUGH; case 0: break; } @@ -761,7 +784,9 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core::initMedium( // allocate one extra Char for the terminating null. auto const allocSize = goodMallocSize((1 + size) * sizeof(Char)); ml_.data_ = static_cast(checkedMalloc(allocSize)); - fbstring_detail::podCopy(data, data + size, ml_.data_); + if (FBSTRING_LIKELY(size > 0)) { + fbstring_detail::podCopy(data, data + size, ml_.data_); + } ml_.size_ = size; ml_.setCapacity(allocSize / sizeof(Char) - 1, Category::isMedium); ml_.data_[size] = '\0'; @@ -821,7 +846,7 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core::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); } @@ -837,16 +862,29 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core::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(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(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 @@ -856,17 +894,29 @@ FOLLY_MALLOC_NOINLINE inline void fbstring_core::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(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(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 @@ -939,7 +989,7 @@ inline void fbstring_core::shrinkLarge(const size_t delta) { */ template class dummy_fbstring_core { -public: + public: dummy_fbstring_core() { } dummy_fbstring_core(const dummy_fbstring_core& another) @@ -982,7 +1032,7 @@ public: backend_.reserve(minCapacity); } -private: + private: std::basic_string backend_; }; #endif // !_LIBSTDCXX_FBSTRING @@ -995,10 +1045,11 @@ private: #ifdef _LIBSTDCXX_FBSTRING template #else -template , - class A = std::allocator, - class Storage = fbstring_core > +template < + typename E, + class T = std::char_traits, + class A = std::allocator, + class Storage = fbstring_core> #endif class basic_fbstring { static void enforce( @@ -1063,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; @@ -1072,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) @@ -1104,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 + /* implicit */ basic_fbstring(const std::basic_string& str) + : store_(str.data(), str.size()) {} #endif basic_fbstring(const basic_fbstring& str, @@ -1144,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 @@ -1168,13 +1219,14 @@ public: #ifndef _LIBSTDCXX_FBSTRING // Compatibility with std::string - basic_fbstring & operator=(const std::string & rhs) { + template + basic_fbstring& operator=(const std::basic_string& rhs) { return assign(rhs.data(), rhs.size()); } // Compatibility with std::string - std::string toStdString() const { - return std::string(data(), size()); + std::basic_string toStdString() const { + return std::basic_string(data(), size()); } #else // A lot of code in fbcode still uses this method, so keep it here for now. @@ -1187,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 std::enable_if< + std::is_same< + typename std::decay::type, + typename folly::basic_fbstring::value_type>::value, + basic_fbstring&>::type + operator=(TP c); basic_fbstring& operator=(std::initializer_list il) { return assign(il.begin(), il.end()); @@ -1336,7 +1405,7 @@ public: basic_fbstring& append(size_type n, value_type c); - template + template basic_fbstring& append(InputIterator first, InputIterator last) { insert(end(), first, last); return *this; @@ -1351,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()); } @@ -1427,29 +1498,29 @@ public: } #endif -private: - iterator - insertImplDiscr(const_iterator i, size_type n, value_type c, std::true_type); - - template - iterator - insertImplDiscr(const_iterator i, InputIter b, InputIter e, std::false_type); - - template - iterator insertImpl( - const_iterator i, - FwdIterator s1, - FwdIterator s2, - std::forward_iterator_tag); - - template - 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 + iterator + insertImplDiscr(const_iterator i, InputIter b, InputIter e, std::false_type); + + template + iterator insertImpl( + const_iterator i, + FwdIterator s1, + FwdIterator s2, + std::forward_iterator_tag); + + template + iterator insertImpl( + const_iterator i, + InputIterator b, + InputIterator e, + std::input_iterator_tag); + + public: template iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) { using Sel = std::integral_constant< @@ -1533,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); - - basic_fbstring& replaceImplDiscr( - iterator i1, - iterator i2, - size_type n2, - value_type c, - std::integral_constant); - - template - basic_fbstring& replaceImplDiscr( - iterator i1, - iterator i2, - InputIter b, - InputIter e, - std::integral_constant); - -private: - template - 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); + + basic_fbstring& replaceImplDiscr( + iterator i1, + iterator i2, + size_type n2, + value_type c, + std::integral_constant); + + template + basic_fbstring& replaceImplDiscr( + iterator i1, + iterator i2, + InputIter b, + InputIter e, + std::integral_constant); + + private: + template + bool replaceAliased( + iterator /* i1 */, + iterator /* i2 */, + FwdIterator /* s1 */, + FwdIterator /* s2 */, + std::false_type) { return false; } @@ -1774,7 +1846,7 @@ private: return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; } -private: + private: // Data Storage store_; }; @@ -1817,8 +1889,13 @@ inline basic_fbstring& basic_fbstring::operator=( } template -inline basic_fbstring& basic_fbstring::operator=( - const value_type c) { +template +inline typename std::enable_if< + std::is_same< + typename std::decay::type, + typename basic_fbstring::value_type>::value, + basic_fbstring&>::type +basic_fbstring::operator=(TP c) { Invariant checker(*this); if (empty()) { @@ -2256,7 +2333,7 @@ basic_fbstring::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(); } } @@ -2271,7 +2348,7 @@ basic_fbstring::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()) { @@ -2289,7 +2366,7 @@ basic_fbstring::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(); } } @@ -2305,7 +2382,7 @@ basic_fbstring::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()) { @@ -2600,13 +2677,13 @@ std::basic_istream< std::basic_istream::value_type, typename basic_fbstring::traits_type>& is, basic_fbstring& str) { - typename std::basic_istream::sentry sentry(is); - typedef std::basic_istream::value_type, - typename basic_fbstring::traits_type> - __istream_type; - typedef typename __istream_type::ios_base __ios_base; + typedef std::basic_istream< + typename basic_fbstring::value_type, + typename basic_fbstring::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) { @@ -2615,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; } @@ -2627,7 +2704,7 @@ std::basic_istream< } } if (!extracted) { - err |= __ios_base::failbit; + err |= _istream_type::failbit; } if (err) { is.setstate(err); @@ -2644,28 +2721,31 @@ operator<<( typename basic_fbstring::traits_type>& os, const basic_fbstring& str) { #if _LIBCPP_VERSION - typename std::basic_ostream< - typename basic_fbstring::value_type, - typename basic_fbstring::traits_type>::sentry __s(os); - if (__s) { + typedef std::basic_ostream< + typename basic_fbstring::value_type, + typename basic_fbstring::traits_type> + _ostream_type; + typename _ostream_type::sentry _s(os); + if (_s) { typedef std::ostreambuf_iterator< typename basic_fbstring::value_type, typename basic_fbstring::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(str.size())); #else std::__ostream_insert(os, str.data(), str.size()); #endif @@ -2679,34 +2759,90 @@ constexpr typename basic_fbstring::size_type #ifndef _LIBSTDCXX_FBSTRING // basic_string compatibility routines -template -inline -bool operator==(const basic_fbstring& lhs, - const std::string& rhs) { +template +inline bool operator==( + const basic_fbstring& lhs, + const std::basic_string& rhs) { return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0; } -template -inline -bool operator==(const std::string& lhs, - const basic_fbstring& rhs) { +template +inline bool operator==( + const std::basic_string& lhs, + const basic_fbstring& rhs) { return rhs == lhs; } -template -inline -bool operator!=(const basic_fbstring& lhs, - const std::string& rhs) { +template +inline bool operator!=( + const basic_fbstring& lhs, + const std::basic_string& rhs) { return !(lhs == rhs); } -template -inline -bool operator!=(const std::string& lhs, - const basic_fbstring& rhs) { +template +inline bool operator!=( + const std::basic_string& lhs, + const basic_fbstring& rhs) { return !(lhs == rhs); } +template +inline bool operator<( + const basic_fbstring& lhs, + const std::basic_string& rhs) { + return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) < 0; +} + +template +inline bool operator>( + const basic_fbstring& lhs, + const std::basic_string& rhs) { + return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) > 0; +} + +template +inline bool operator<( + const std::basic_string& lhs, + const basic_fbstring& rhs) { + return rhs > lhs; +} + +template +inline bool operator>( + const std::basic_string& lhs, + const basic_fbstring& rhs) { + return rhs < lhs; +} + +template +inline bool operator<=( + const basic_fbstring& lhs, + const std::basic_string& rhs) { + return !(lhs > rhs); +} + +template +inline bool operator>=( + const basic_fbstring& lhs, + const std::basic_string& rhs) { + return !(lhs < rhs); +} + +template +inline bool operator<=( + const std::basic_string& lhs, + const basic_fbstring& rhs) { + return !(lhs > rhs); +} + +template +inline bool operator>=( + const std::basic_string& lhs, + const basic_fbstring& rhs) { + return !(lhs < rhs); +} + #if !defined(_LIBSTDCXX_FBSTRING) typedef basic_fbstring fbstring; #endif @@ -2715,11 +2851,9 @@ typedef basic_fbstring fbstring; template FOLLY_ASSUME_RELOCATABLE(basic_fbstring); -#else -_GLIBCXX_END_NAMESPACE_VERSION #endif -} // namespace folly +FOLLY_FBSTRING_END_NAMESPACE #ifndef _LIBSTDCXX_FBSTRING @@ -2747,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 @@ -2772,3 +2896,13 @@ FOLLY_FBSTRING_HASH #undef FBSTRING_LIKELY #undef FBSTRING_UNLIKELY #undef FBSTRING_ASSERT + +#ifndef _LIBSTDCXX_FBSTRING +namespace folly { +template +struct IsSomeString; + +template <> +struct IsSomeString : std::true_type {}; +} // namespace folly +#endif