/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2011-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#pragma once
#include <atomic>
+#include <cstddef>
+#include <iosfwd>
#include <limits>
#include <type_traits>
#pragma GCC system_header
-// When used as std::string replacement always disable assertions.
-#ifndef NDEBUG
-#define NDEBUG
-#define FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
-#endif // NDEBUG
+#include "basic_fbstring_malloc.h" // @manual
-#include "basic_fbstring_malloc.h"
+// 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>
+#include <folly/hash/Hash.h>
+#include <folly/memory/Malloc.h>
+#include <folly/portability/BitsFunctexcept.h>
-#if FOLLY_HAVE_DEPRECATED_ASSOC
-#ifdef _GLIBCXX_SYMVER
-#include <ext/hash_set>
-#include <ext/hash_map>
-#endif
-#endif
+// When used in folly, assertions are not disabled.
+#define FBSTRING_ASSERT(expr) assert(expr)
#endif
#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
template <class Pod, class T>
inline void podFill(Pod* b, Pod* e, T c) {
- assert(b && e && b <= e);
- /*static*/ if (sizeof(T) == 1) {
- memset(b, c, e - b);
+ FBSTRING_ASSERT(b && e && b <= e);
+ 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) {
*/
template <class Pod>
inline void podCopy(const Pod* b, const Pod* e, Pod* d) {
- assert(e >= b);
- assert(d >= e || d + (e - b) <= b);
+ 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));
}
*/
template <class Pod>
inline void podMove(const Pod* b, const Pod* e, Pod* d) {
- assert(e >= b);
+ FBSTRING_ASSERT(e >= b);
memmove(d, b, (e - b) * sizeof(*b));
}
template <class Char>
class fbstring_core_model {
-public:
+ public:
fbstring_core_model();
fbstring_core_model(const fbstring_core_model &);
~fbstring_core_model();
// 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 &);
};
* 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
static constexpr auto kIsLittleEndian =
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__;
#endif
-public:
+ public:
fbstring_core() noexcept { reset(); }
fbstring_core(const fbstring_core & rhs) {
- assert(&rhs != this);
+ FBSTRING_ASSERT(&rhs != this);
switch (rhs.category()) {
case Category::isSmall:
copySmall(rhs);
default:
fbstring_detail::assume_unreachable();
}
- assert(size() == rhs.size());
- assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
+ FBSTRING_ASSERT(size() == rhs.size());
+ FBSTRING_ASSERT(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
}
fbstring_core(fbstring_core&& goner) noexcept {
} else {
initLarge(data, size);
}
-#ifndef NDEBUG
-#ifndef _LIBSTDCXX_FBSTRING
- assert(this->size() == size);
- assert(size == 0 || memcmp(this->data(), data, size * sizeof(Char)) == 0);
-#endif
-#endif
+ FBSTRING_ASSERT(this->size() == size);
+ FBSTRING_ASSERT(
+ size == 0 || memcmp(this->data(), data, size * sizeof(Char)) == 0);
}
~fbstring_core() noexcept {
- auto const c = category();
- if (c == Category::isSmall) {
- return;
- }
- if (c == Category::isMedium) {
- free(ml_.data_);
+ if (category() == Category::isSmall) {
return;
}
- RefCounted::decrementRefs(ml_.data_);
+ destroyMediumLarge();
}
// Snatches a previously mallocated string. The parameter "size"
const size_t allocatedSize,
AcquireMallocatedString) {
if (size > 0) {
- assert(allocatedSize >= size + 1);
- assert(data[size] == '\0');
+ FBSTRING_ASSERT(allocatedSize >= size + 1);
+ FBSTRING_ASSERT(data[size] == '\0');
// Use the medium string storage
ml_.data_ = data;
ml_.size_ = size;
fbstring_detail::assume_unreachable();
}
- const Char * c_str() const {
- auto const c = category();
- if (c == Category::isSmall) {
- assert(small_[smallSize()] == '\0');
- return small_;
- }
- assert(c == Category::isMedium || c == Category::isLarge);
- assert(ml_.data_[ml_.size_] == '\0');
- return ml_.data_;
+ const Char* c_str() const {
+ const Char* ptr = ml_.data_;
+ // With this syntax, GCC and Clang generate a CMOV instead of a branch.
+ ptr = (category() == Category::isSmall) ? small_ : ptr;
+ return ptr;
}
void shrink(const size_t delta) {
}
}
+ FOLLY_MALLOC_NOINLINE
void reserve(size_t minCapacity, bool disableSSO = FBSTRING_DISABLE_SSO) {
switch (category()) {
case Category::isSmall:
default:
fbstring_detail::assume_unreachable();
}
- assert(capacity() >= minCapacity);
+ FBSTRING_ASSERT(capacity() >= minCapacity);
}
Char* expandNoinit(
}
size_t size() const {
- return category() == Category::isSmall ? smallSize() : ml_.size_;
+ size_t ret = ml_.size_;
+ /* static */ if (kIsLittleEndian) {
+ // We can save a couple instructions, because the category is
+ // small iff the last char, as unsigned, is <= maxSmallSize.
+ typedef typename std::make_unsigned<Char>::type UChar;
+ auto maybeSmallSize = size_t(maxSmallSize) -
+ size_t(static_cast<UChar>(small_[maxSmallSize]));
+ // With this syntax, GCC and Clang generate a CMOV instead of a branch.
+ ret = (static_cast<ssize_t>(maybeSmallSize) >= 0) ? maybeSmallSize : ret;
+ } else {
+ ret = (category() == Category::isSmall) ? smallSize() : ret;
+ }
+ return ret;
}
size_t capacity() const {
if (RefCounted::refs(ml_.data_) > 1) {
return ml_.size_;
}
- default: {}
+ break;
+ default:
+ break;
}
return ml_.capacity();
}
return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1;
}
-private:
+ private:
// Disabled
fbstring_core & operator=(const fbstring_core & rhs);
- // Equivalent to setSmallSize(0) but a few ns faster in
- // microbenchmarks.
void reset() {
- ml_.capacity_ = kIsLittleEndian
- ? maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)))
- : maxSmallSize << 2;
- small_[0] = '\0';
- assert(category() == Category::isSmall && size() == 0);
+ setSmallSize(0);
+ }
+
+ FOLLY_MALLOC_NOINLINE void destroyMediumLarge() noexcept {
+ auto const c = category();
+ FBSTRING_ASSERT(c != Category::isSmall);
+ if (c == Category::isMedium) {
+ free(ml_.data_);
+ } else {
+ RefCounted::decrementRefs(ml_.data_);
+ }
}
struct RefCounted {
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) {
static void decrementRefs(Char * p) {
auto const dis = fromData(p);
size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel);
- assert(oldcnt > 0);
+ FBSTRING_ASSERT(oldcnt > 0);
if (oldcnt == 1) {
free(dis);
}
}
static RefCounted * create(size_t * size) {
- // Don't forget to allocate one extra Char for the terminating
- // null. In this case, however, one Char is already part of the
- // struct.
- const size_t allocSize = goodMallocSize(
- sizeof(RefCounted) + *size * sizeof(Char));
+ 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;
}
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) {
- 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);
- assert(dis->refCount_.load(std::memory_order_acquire) == 1);
- // Don't forget to allocate one extra Char for the terminating
- // null. In this case, however, one Char is already part of the
- // struct.
- auto result = static_cast<RefCounted*>(
- smartRealloc(dis,
- sizeof(RefCounted) + currentSize * sizeof(Char),
- sizeof(RefCounted) + currentCapacity * sizeof(Char),
- sizeof(RefCounted) + newCapacity * sizeof(Char)));
- assert(result->refCount_.load(std::memory_order_acquire) == 1);
+ FBSTRING_ASSERT(dis->refCount_.load(std::memory_order_acquire) == 1);
+ 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;
}
};
- typedef std::conditional<sizeof(size_t) == 4, uint32_t, uint64_t>::type
- category_type;
+ typedef uint8_t category_type;
enum class Category : category_type {
isSmall = 0,
- isMedium = kIsLittleEndian
- ? sizeof(size_t) == 4 ? 0x80000000 : 0x8000000000000000
- : 0x2,
- isLarge = kIsLittleEndian
- ? sizeof(size_t) == 4 ? 0x40000000 : 0x4000000000000000
- : 0x1,
+ isMedium = kIsLittleEndian ? 0x80 : 0x2,
+ isLarge = kIsLittleEndian ? 0x40 : 0x1,
};
Category category() const {
// works for both big-endian and little-endian
- return static_cast<Category>(ml_.capacity_ & categoryExtractMask);
+ return static_cast<Category>(bytes_[lastChar] & categoryExtractMask);
}
struct MediumLarge {
}
void setCapacity(size_t cap, Category cat) {
- capacity_ = kIsLittleEndian
- ? cap | static_cast<category_type>(cat)
- : (cap << 2) | static_cast<category_type>(cat);
+ capacity_ = kIsLittleEndian
+ ? cap | (static_cast<size_t>(cat) << kCategoryShift)
+ : (cap << 2) | static_cast<size_t>(cat);
}
};
union {
+ uint8_t bytes_[sizeof(MediumLarge)]; // For accessing the last byte.
Char small_[sizeof(MediumLarge) / sizeof(Char)];
MediumLarge ml_;
};
- enum : size_t {
- lastChar = sizeof(MediumLarge) - 1,
- maxSmallSize = lastChar / sizeof(Char),
- maxMediumSize = 254 / sizeof(Char), // coincides with the small
- // bin size in dlmalloc
- categoryExtractMask = kIsLittleEndian
- ? sizeof(size_t) == 4 ? 0xC0000000 : size_t(0xC000000000000000)
- : 0x3,
- capacityExtractMask = kIsLittleEndian
- ? ~categoryExtractMask
- : 0x0 /*unused*/,
- };
+ constexpr static size_t lastChar = sizeof(MediumLarge) - 1;
+ constexpr static size_t maxSmallSize = lastChar / sizeof(Char);
+ constexpr static size_t maxMediumSize = 254 / sizeof(Char);
+ constexpr static uint8_t categoryExtractMask = kIsLittleEndian ? 0xC0 : 0x3;
+ constexpr static size_t kCategoryShift = (sizeof(size_t) - 1) * 8;
+ constexpr static size_t capacityExtractMask = kIsLittleEndian
+ ? ~(size_t(categoryExtractMask) << kCategoryShift)
+ : 0x0 /* unused */;
+
static_assert(!(sizeof(MediumLarge) % sizeof(Char)),
"Corrupt memory layout for fbstring.");
size_t smallSize() const {
- assert(category() == Category::isSmall);
+ FBSTRING_ASSERT(category() == Category::isSmall);
constexpr auto shift = kIsLittleEndian ? 0 : 2;
auto smallShifted = static_cast<size_t>(small_[maxSmallSize]) >> shift;
- assert(static_cast<size_t>(maxSmallSize) >= smallShifted);
+ FBSTRING_ASSERT(static_cast<size_t>(maxSmallSize) >= smallShifted);
return static_cast<size_t>(maxSmallSize) - smallShifted;
}
// Warning: this should work with uninitialized strings too,
// so don't assume anything about the previous value of
// small_[maxSmallSize].
- assert(s <= 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';
- assert(category() == Category::isSmall && size() == s);
+ FBSTRING_ASSERT(category() == Category::isSmall && size() == s);
}
void copySmall(const fbstring_core&);
void shrinkMedium(size_t delta);
void shrinkLarge(size_t delta);
+ void unshare(size_t minCapacity = 0);
Char* mutableDataLarge();
};
// which stores a short string's length, is shared with the
// ml_.capacity field).
ml_ = rhs.ml_;
- assert(category() == Category::isSmall && this->size() == rhs.size());
+ FBSTRING_ASSERT(
+ category() == Category::isSmall && this->size() == rhs.size());
}
template <class Char>
-inline void fbstring_core<Char>::copyMedium(const fbstring_core& rhs) {
+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.
auto const allocSize = goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));
rhs.ml_.data_, rhs.ml_.data_ + rhs.ml_.size_ + 1, ml_.data_);
ml_.size_ = rhs.ml_.size_;
ml_.setCapacity(allocSize / sizeof(Char) - 1, Category::isMedium);
- assert(category() == Category::isMedium);
+ FBSTRING_ASSERT(category() == Category::isMedium);
}
template <class Char>
-inline void fbstring_core<Char>::copyLarge(const fbstring_core& rhs) {
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::copyLarge(
+ const fbstring_core& rhs) {
// Large strings are just refcounted
ml_ = rhs.ml_;
RefCounted::incrementRefs(ml_.data_);
- assert(category() == Category::isLarge && size() == rhs.size());
+ FBSTRING_ASSERT(category() == Category::isLarge && size() == rhs.size());
}
// Small strings are bitblitted
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;
}
}
template <class Char>
-inline void fbstring_core<Char>::initMedium(
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::initMedium(
const Char* const data, const size_t size) {
// Medium strings are allocated normally. Don't forget to
// allocate one extra Char for the terminating null.
auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));
ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
- fbstring_detail::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';
}
template <class Char>
-inline void fbstring_core<Char>::initLarge(
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::initLarge(
const Char* const data, const size_t size) {
// Large strings are allocated differently
size_t effectiveCapacity = size;
ml_.data_[size] = '\0';
}
+template <class Char>
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::unshare(
+ size_t minCapacity) {
+ FBSTRING_ASSERT(category() == Category::isLarge);
+ size_t effectiveCapacity = std::max(minCapacity, ml_.capacity());
+ auto const newRC = RefCounted::create(&effectiveCapacity);
+ // If this fails, someone placed the wrong capacity in an
+ // fbstring.
+ FBSTRING_ASSERT(effectiveCapacity >= ml_.capacity());
+ // Also copies terminator.
+ fbstring_detail::podCopy(ml_.data_, ml_.data_ + ml_.size_ + 1, newRC->data_);
+ RefCounted::decrementRefs(ml_.data_);
+ ml_.data_ = newRC->data_;
+ ml_.setCapacity(effectiveCapacity, Category::isLarge);
+ // size_ remains unchanged.
+}
+
template <class Char>
inline Char* fbstring_core<Char>::mutableDataLarge() {
- assert(category() == Category::isLarge);
- if (RefCounted::refs(ml_.data_) > 1) {
- // Ensure unique.
- size_t effectiveCapacity = ml_.capacity();
- auto const newRC = RefCounted::create(&effectiveCapacity);
- // If this fails, someone placed the wrong capacity in an
- // fbstring.
- assert(effectiveCapacity >= ml_.capacity());
- // Also copies terminator.
- fbstring_detail::podCopy(
- ml_.data_, ml_.data_ + ml_.size_ + 1, newRC->data_);
- RefCounted::decrementRefs(ml_.data_);
- ml_.data_ = newRC->data_;
+ FBSTRING_ASSERT(category() == Category::isLarge);
+ if (RefCounted::refs(ml_.data_) > 1) { // Ensure unique.
+ unshare();
}
return ml_.data_;
}
template <class Char>
-inline void fbstring_core<Char>::reserveLarge(size_t minCapacity) {
- assert(category() == Category::isLarge);
- // Ensure unique
- if (RefCounted::refs(ml_.data_) > 1) {
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::reserveLarge(
+ size_t minCapacity) {
+ FBSTRING_ASSERT(category() == Category::isLarge);
+ if (RefCounted::refs(ml_.data_) > 1) { // Ensure unique
// We must make it unique regardless; in-place reallocation is
// useless if the string is shared. In order to not surprise
// people, reserve the new block at current capacity or
// more. That way, a string's capacity never shrinks after a
// call to reserve.
- minCapacity = std::max(minCapacity, ml_.capacity());
- auto const newRC = RefCounted::create(&minCapacity);
- // Also copies terminator.
- fbstring_detail::podCopy(
- ml_.data_, ml_.data_ + ml_.size_ + 1, newRC->data_);
- RefCounted::decrementRefs(ml_.data_);
- ml_.data_ = newRC->data_;
- ml_.setCapacity(minCapacity, Category::isLarge);
- // size remains unchanged
+ unshare(minCapacity);
} else {
// String is not shared, so let's try to realloc (if needed)
if (minCapacity > ml_.capacity()) {
// Asking for more memory
auto const newRC = RefCounted::reallocate(
- ml_.data_, ml_.size_, ml_.capacity(), minCapacity);
+ ml_.data_, ml_.size_, ml_.capacity(), &minCapacity);
ml_.data_ = newRC->data_;
ml_.setCapacity(minCapacity, Category::isLarge);
}
- assert(capacity() >= minCapacity);
+ FBSTRING_ASSERT(capacity() >= minCapacity);
}
}
template <class Char>
-inline void fbstring_core<Char>::reserveMedium(const size_t minCapacity) {
- assert(category() == Category::isMedium);
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::reserveMedium(
+ const size_t minCapacity) {
+ FBSTRING_ASSERT(category() == Category::isMedium);
// String is not shared
if (minCapacity <= ml_.capacity()) {
return; // nothing to do, there's enough room
fbstring_detail::podCopy(
ml_.data_, ml_.data_ + ml_.size_ + 1, nascent.ml_.data_);
nascent.swap(*this);
- assert(capacity() >= minCapacity);
+ FBSTRING_ASSERT(capacity() >= minCapacity);
}
}
template <class Char>
-inline void fbstring_core<Char>::reserveSmall(
+FOLLY_MALLOC_NOINLINE inline void fbstring_core<Char>::reserveSmall(
size_t minCapacity, const bool disableSSO) {
- assert(category() == Category::isSmall);
+ FBSTRING_ASSERT(category() == Category::isSmall);
if (!disableSSO && minCapacity <= maxSmallSize) {
// small
// Nothing to do, everything stays put
ml_.data_ = newRC->data_;
ml_.size_ = size;
ml_.setCapacity(minCapacity, Category::isLarge);
- assert(capacity() >= minCapacity);
+ FBSTRING_ASSERT(capacity() >= minCapacity);
}
}
bool expGrowth, /* = false */
bool disableSSO /* = FBSTRING_DISABLE_SSO */) {
// Strategy is simple: make room, then change size
- assert(capacity() >= size());
+ FBSTRING_ASSERT(capacity() >= size());
size_t sz, newSz;
if (category() == Category::isSmall) {
sz = smallSize();
reserve(expGrowth ? std::max(newSz, 1 + capacity() * 3 / 2) : newSz);
}
}
- assert(capacity() >= newSz);
+ FBSTRING_ASSERT(capacity() >= newSz);
// Category can't be small - we took care of that above
- assert(category() == Category::isMedium || category() == Category::isLarge);
+ FBSTRING_ASSERT(
+ category() == Category::isMedium || category() == Category::isLarge);
ml_.size_ = newSz;
ml_.data_[newSz] = '\0';
- assert(size() == newSz);
+ FBSTRING_ASSERT(size() == newSz);
return ml_.data_ + sz;
}
template <class Char>
inline void fbstring_core<Char>::shrinkSmall(const size_t delta) {
// Check for underflow
- assert(delta <= smallSize());
+ FBSTRING_ASSERT(delta <= smallSize());
setSmallSize(smallSize() - delta);
}
inline void fbstring_core<Char>::shrinkMedium(const size_t delta) {
// Medium strings and unique large strings need no special
// handling.
- assert(ml_.size_ >= delta);
+ FBSTRING_ASSERT(ml_.size_ >= delta);
ml_.size_ -= delta;
ml_.data_[ml_.size_] = '\0';
}
template <class Char>
inline void fbstring_core<Char>::shrinkLarge(const size_t delta) {
- assert(ml_.size_ >= delta);
+ FBSTRING_ASSERT(ml_.size_ >= delta);
// Shared large string, must make unique. This is because of the
// durn terminator must be written, which may trample the shared
// data.
*/
template <class Char>
class dummy_fbstring_core {
-public:
+ public:
dummy_fbstring_core() {
}
dummy_fbstring_core(const dummy_fbstring_core& another)
return const_cast<Char*>(backend_.data());
}
void shrink(size_t delta) {
- assert(delta <= size());
+ FBSTRING_ASSERT(delta <= size());
backend_.resize(size() - delta);
}
Char* expandNoinit(size_t delta) {
backend_.reserve(minCapacity);
}
-private:
+ private:
std::basic_string<Char> backend_;
};
#endif // !_LIBSTDCXX_FBSTRING
#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(
bool condition,
void (*throw_exc)(const char*),
begin()[size()] == '\0';
}
- struct Invariant;
- friend struct Invariant;
struct Invariant {
Invariant& operator=(const Invariant&) = delete;
-#ifndef NDEBUG
- explicit Invariant(const basic_fbstring& s) : s_(s) {
- assert(s_.isSane());
+ explicit Invariant(const basic_fbstring& s) noexcept : s_(s) {
+ FBSTRING_ASSERT(s_.isSane());
}
- ~Invariant() {
- assert(s_.isSane());
+ ~Invariant() noexcept {
+ FBSTRING_ASSERT(s_.isSane());
}
- private:
+
+ private:
const basic_fbstring& s_;
-#else
- explicit Invariant(const basic_fbstring&) {}
-#endif
};
-public:
+ public:
// types
typedef T traits_type;
typedef typename traits_type::char_type value_type;
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;
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)
#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,
assign(str, pos, n);
}
+ FOLLY_MALLOC_NOINLINE
/* implicit */ basic_fbstring(const value_type* s, const A& /*a*/ = A())
- : store_(s, basic_fbstring::traitsLength(s)) {
- }
+ : store_(s, traitsLength(s)) {}
+ FOLLY_MALLOC_NOINLINE
basic_fbstring(const value_type* s, size_type n, const A& /*a*/ = A())
: store_(s, n) {
}
+ FOLLY_MALLOC_NOINLINE
basic_fbstring(size_type n, value_type c, const A& /*a*/ = A()) {
auto const pData = store_.expandNoinit(n);
fbstring_detail::podFill(pData, pData + n, c);
}
template <class InIt>
- basic_fbstring(
+ FOLLY_MALLOC_NOINLINE basic_fbstring(
InIt begin,
InIt end,
typename std::enable_if<
}
// 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
}
// Construction from initialization list
+ FOLLY_MALLOC_NOINLINE
basic_fbstring(std::initializer_list<value_type> il) {
assign(il.begin(), il.end());
}
- ~basic_fbstring() noexcept {
- }
+ ~basic_fbstring() noexcept {}
basic_fbstring& operator=(const basic_fbstring& lhs);
#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.
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());
// C++11 21.4.5, element access:
const value_type& front() const { return *begin(); }
const value_type& back() const {
- assert(!empty());
+ FBSTRING_ASSERT(!empty());
// Should be begin()[size() - 1], but that branches twice
return *(end() - 1);
}
value_type& front() { return *begin(); }
value_type& back() {
- assert(!empty());
+ FBSTRING_ASSERT(!empty());
// Should be begin()[size() - 1], but that branches twice
return *(end() - 1);
}
void pop_back() {
- assert(!empty());
+ FBSTRING_ASSERT(!empty());
store_.shrink(1);
}
basic_fbstring& append(const value_type* s, size_type n);
basic_fbstring& append(const value_type* s) {
- return append(s, traits_type::length(s));
+ return append(s, traitsLength(s));
}
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;
}
basic_fbstring& assign(const basic_fbstring& str) {
- if (&str == this) return *this;
+ if (&str == this) {
+ return *this;
+ }
return assign(str.data(), str.size());
}
basic_fbstring& assign(const value_type* s, const size_type n);
basic_fbstring& assign(const value_type* s) {
- return assign(s, traits_type::length(s));
+ return assign(s, traitsLength(s));
}
basic_fbstring& assign(std::initializer_list<value_type> il) {
}
basic_fbstring& insert(size_type pos, const value_type* s) {
- return insert(pos, s, traits_type::length(s));
+ return insert(pos, s, traitsLength(s));
}
basic_fbstring& insert(size_type pos, size_type n, value_type c) {
}
#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<
// Replaces at most n1 chars of *this, starting with pos, with chars from s
basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
- return replace(pos, n1, s, traits_type::length(s));
+ return replace(pos, n1, s, traitsLength(s));
}
// Replaces at most n1 chars of *this, starting with pos, with n2
}
basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
- return replace(i1, i2, s, traits_type::length(s));
- }
-
-private:
- basic_fbstring& replaceImplDiscr(
- iterator i1,
- iterator i2,
- const value_type* s,
- size_type n,
- 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 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) {
return false;
}
const;
size_type find(const value_type* s, size_type pos = 0) const {
- return find(s, pos, traits_type::length(s));
+ return find(s, pos, traitsLength(s));
}
size_type find (value_type c, size_type pos = 0) 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 {
- return rfind(s, pos, traits_type::length(s));
+ return rfind(s, pos, traitsLength(s));
}
size_type rfind(value_type c, size_type pos = npos) const {
const;
size_type find_first_of(const value_type* s, size_type pos = 0) const {
- return find_first_of(s, pos, traits_type::length(s));
+ return find_first_of(s, pos, traitsLength(s));
}
size_type find_first_of(value_type c, size_type pos = 0) const {
size_type find_last_of (const value_type* s,
size_type pos = npos) const {
- return find_last_of(s, pos, traits_type::length(s));
+ return find_last_of(s, pos, traitsLength(s));
}
size_type find_last_of (value_type c, size_type pos = npos) const {
size_type find_first_not_of(const value_type* s,
size_type pos = 0) const {
- return find_first_not_of(s, pos, traits_type::length(s));
+ return find_first_not_of(s, pos, traitsLength(s));
}
size_type find_first_not_of(value_type c, size_type pos = 0) const {
size_type find_last_not_of(const value_type* s,
size_type pos = npos) const {
- return find_last_not_of(s, pos, traits_type::length(s));
+ return find_last_not_of(s, pos, traitsLength(s));
}
size_type find_last_not_of (value_type c, size_type pos = npos) const {
int compare(size_type pos1, size_type n1,
const value_type* s) const {
- return compare(pos1, n1, s, traits_type::length(s));
+ return compare(pos1, n1, s, traitsLength(s));
}
int compare(size_type pos1, size_type n1,
// Code from Jean-Francois Bastien (03/26/2007)
int compare(const value_type* s) const {
- // Could forward to compare(0, size(), s, traits_type::length(s))
+ // Could forward to compare(0, size(), s, traitsLength(s))
// but that does two extra checks
- const size_type n1(size()), n2(traits_type::length(s));
+ const size_type n1(size()), n2(traitsLength(s));
const int r = traits_type::compare(data(), s, std::min(n1, n2));
return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
}
-private:
+ private:
// Data
Storage store_;
};
template <typename E, class T, class A, class S>
-inline typename basic_fbstring<E, T, A, S>::size_type
+FOLLY_MALLOC_NOINLINE inline typename basic_fbstring<E, T, A, S>::size_type
basic_fbstring<E, T, A, S>::traitsLength(const value_type* s) {
return s ? traits_type::length(s)
: (std::__throw_logic_error(
}
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()) {
auto pData = store_.expandNoinit(delta);
fbstring_detail::podFill(pData, pData + delta, c);
}
- assert(this->size() == n);
+ FBSTRING_ASSERT(this->size() == n);
}
template <typename E, class T, class A, class S>
auto desiredSize = size() + str.size();
#endif
append(str.data(), str.size());
- assert(size() == desiredSize);
+ FBSTRING_ASSERT(size() == desiredSize);
return *this;
}
}
template <typename E, class T, class A, class S>
-inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(
- const value_type* s, size_type n) {
+FOLLY_MALLOC_NOINLINE inline basic_fbstring<E, T, A, S>&
+basic_fbstring<E, T, A, S>::append(const value_type* s, size_type n) {
Invariant checker(*this);
if (FBSTRING_UNLIKELY(!n)) {
// info.
std::less_equal<const value_type*> le;
if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
- assert(le(s + n, oldData + oldSize));
+ FBSTRING_ASSERT(le(s + n, oldData + oldSize));
// expandNoinit() could have moved the storage, restore the source.
s = data() + (s - oldData);
fbstring_detail::podMove(s, s + n, pData);
fbstring_detail::podCopy(s, s + n, pData);
}
- assert(size() == oldSize + n);
+ FBSTRING_ASSERT(size() == oldSize + n);
return *this;
}
}
template <typename E, class T, class A, class S>
-inline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::assign(
- const value_type* s, const size_type n) {
+FOLLY_MALLOC_NOINLINE inline basic_fbstring<E, T, A, S>&
+basic_fbstring<E, T, A, S>::assign(const value_type* s, const size_type n) {
Invariant checker(*this);
- // s can alias this, we need to use podMove.
if (n == 0) {
resize(0);
} else if (size() >= n) {
// s can alias this, we need to use podMove.
fbstring_detail::podMove(s, s + n, store_.mutableData());
store_.shrink(size() - n);
- assert(size() == n);
+ FBSTRING_ASSERT(size() == n);
} else {
// If n is larger than size(), s cannot alias this string's
// storage.
fbstring_detail::podCopy(s, s + n, store_.expandNoinit(n));
}
- assert(size() == n);
+ FBSTRING_ASSERT(size() == n);
return *this;
}
break;
}
- assert(size == this->size());
- assert(size == capacity());
+ FBSTRING_ASSERT(size == this->size());
+ FBSTRING_ASSERT(size == capacity());
// Start at minimum allocation 63 + terminator = 64.
reserve(std::max<size_t>(63, 3 * size / 2));
// Clear the error so we can continue reading.
// Here we know that the last char matches
// Continue in pedestrian mode
for (size_t j = 0;;) {
- assert(j < nsize);
+ FBSTRING_ASSERT(j < nsize);
if (i[j] != needle[j]) {
// Not found, we can skip
// Compute the skip value lazily
const_iterator i, size_type n, value_type c, std::true_type) {
Invariant checker(*this);
- assert(i >= cbegin() && i <= cend());
+ FBSTRING_ASSERT(i >= cbegin() && i <= cend());
const size_type pos = i - cbegin();
auto oldSize = size();
std::forward_iterator_tag) {
Invariant checker(*this);
- assert(i >= cbegin() && i <= cend());
+ FBSTRING_ASSERT(i >= cbegin() && i <= cend());
const size_type pos = i - cbegin();
auto n = std::distance(s1, s2);
- assert(n >= 0);
+ FBSTRING_ASSERT(n >= 0);
auto oldSize = size();
store_.expandNoinit(n, /* expGrowth = */ true);
const value_type* s,
size_type n,
std::integral_constant<int, 2>) {
- assert(i1 <= i2);
- assert(begin() <= i1 && i1 <= end());
- assert(begin() <= i2 && i2 <= end());
+ FBSTRING_ASSERT(i1 <= i2);
+ FBSTRING_ASSERT(begin() <= i1 && i1 <= end());
+ FBSTRING_ASSERT(begin() <= i2 && i2 <= end());
return replace(i1, i2, s, s + n);
}
std::fill(i1, i2, c);
insert(i2, n2 - n1, c);
}
- assert(isSane());
+ FBSTRING_ASSERT(isSane());
return *this;
}
}
auto const n1 = i2 - i1;
- assert(n1 >= 0);
+ FBSTRING_ASSERT(n1 >= 0);
auto const n2 = std::distance(s1, s2);
- assert(n2 >= 0);
+ FBSTRING_ASSERT(n2 >= 0);
if (n1 > n2) {
// shrinks
s1 = fbstring_detail::copy_n(s1, n1, i1).first;
insert(i2, s1, s2);
}
- assert(isSane());
+ FBSTRING_ASSERT(isSane());
}
template <typename E, class T, class A, class S>
}
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();
}
}
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()) {
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();
}
}
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()) {
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) {
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;
}
}
}
if (!extracted) {
- err |= __ios_base::failbit;
+ err |= _istream_type::failbit;
}
if (err) {
is.setstate(err);
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
#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
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
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
#undef throw
#undef FBSTRING_LIKELY
#undef FBSTRING_UNLIKELY
+#undef FBSTRING_ASSERT
-#ifdef FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
-#undef NDEBUG
-#undef FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
-#endif // FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
+#ifndef _LIBSTDCXX_FBSTRING
+namespace folly {
+template <class T>
+struct IsSomeString;
+
+template <>
+struct IsSomeString<fbstring> : std::true_type {};
+} // namespace folly
+#endif