#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"
+// When used as std::string replacement always disable assertions.
+#define FBSTRING_ASSERT(expr) /* empty */
+
#else // !_LIBSTDCXX_FBSTRING
#include <folly/Portability.h>
#endif
#endif
+// When used in folly, assertions are not disabled.
+#define FBSTRING_ASSERT(expr) assert(expr)
+
#endif
// We defined these here rather than including Likely.h to avoid
template <class Pod, class T>
inline void podFill(Pod* b, Pod* e, T c) {
- assert(b && e && b <= e);
+ FBSTRING_ASSERT(b && e && b <= e);
/*static*/ if (sizeof(T) == 1) {
memset(b, c, e - b);
} else {
*/
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(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));
}
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 {
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;
const Char * c_str() const {
auto const c = category();
if (c == Category::isSmall) {
- assert(small_[smallSize()] == '\0');
+ FBSTRING_ASSERT(small_[smallSize()] == '\0');
return small_;
}
- assert(c == Category::isMedium || c == Category::isLarge);
- assert(ml_.data_[ml_.size_] == '\0');
+ FBSTRING_ASSERT(c == Category::isMedium || c == Category::isLarge);
+ FBSTRING_ASSERT(ml_.data_[ml_.size_] == '\0');
return ml_.data_;
}
default:
fbstring_detail::assume_unreachable();
}
- assert(capacity() >= minCapacity);
+ FBSTRING_ASSERT(capacity() >= minCapacity);
}
Char* expandNoinit(
? maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)))
: maxSmallSize << 2;
small_[0] = '\0';
- assert(category() == Category::isSmall && size() == 0);
+ FBSTRING_ASSERT(category() == Category::isSmall && size() == 0);
}
struct RefCounted {
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);
}
const size_t currentSize,
const size_t currentCapacity,
const size_t newCapacity) {
- assert(newCapacity > 0 && newCapacity > currentSize);
+ FBSTRING_ASSERT(newCapacity > 0 && newCapacity > currentSize);
auto const dis = fromData(data);
- assert(dis->refCount_.load(std::memory_order_acquire) == 1);
+ 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.
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(result->refCount_.load(std::memory_order_acquire) == 1);
return result;
}
};
"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_[s] = '\0';
- assert(category() == Category::isSmall && size() == s);
+ FBSTRING_ASSERT(category() == Category::isSmall && size() == s);
}
void copySmall(const fbstring_core&);
// 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>
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>
// 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
template <class Char>
inline Char* fbstring_core<Char>::mutableDataLarge() {
- assert(category() == Category::isLarge);
+ FBSTRING_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());
+ FBSTRING_ASSERT(effectiveCapacity >= ml_.capacity());
// Also copies terminator.
fbstring_detail::podCopy(
ml_.data_, ml_.data_ + ml_.size_ + 1, newRC->data_);
template <class Char>
inline void fbstring_core<Char>::reserveLarge(size_t minCapacity) {
- assert(category() == Category::isLarge);
+ FBSTRING_ASSERT(category() == Category::isLarge);
// Ensure unique
if (RefCounted::refs(ml_.data_) > 1) {
// We must make it unique regardless; in-place reallocation is
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);
+ 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(
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.
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) {
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;
// 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);
}
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;
}
// 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;
}
// 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>
#undef throw
#undef FBSTRING_LIKELY
#undef FBSTRING_UNLIKELY
-
-#ifdef FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
-#undef NDEBUG
-#undef FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
-#endif // FOLLY_DEFINED_NDEBUG_FOR_FBSTRING
+#undef FBSTRING_ASSERT