2 * Copyright 2014 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // @author: Andrei Alexandrescu (aalexandre)
20 #ifndef FOLLY_BASE_FBSTRING_H_
21 #define FOLLY_BASE_FBSTRING_H_
24 fbstring's behavior can be configured via two macro definitions, as
25 follows. Normally, fbstring does not write a '\0' at the end of
26 each string whenever it changes the underlying characters. Instead,
27 it lazily writes the '\0' whenever either c_str() or data()
30 This is standard-compliant behavior and may save costs in some
31 circumstances. However, it may be surprising to some client code
32 because c_str() and data() are const member functions (fbstring
33 uses the "mutable" storage class for its own state).
35 In order to appease client code that expects fbstring to be
36 zero-terminated at all times, if the preprocessor symbol
37 FBSTRING_CONSERVATIVE is defined, fbstring does exactly that,
38 i.e. it goes the extra mile to guarantee a '\0' is always planted
39 at the end of its data.
41 On the contrary, if the desire is to debug faulty client code that
42 unduly assumes the '\0' is present, fbstring plants a '^' (i.e.,
43 emphatically NOT a zero) at the end of each string if
44 FBSTRING_PERVERSE is defined. (Calling c_str() or data() still
45 writes the '\0', of course.)
47 The preprocessor symbols FBSTRING_PERVERSE and
48 FBSTRING_CONSERVATIVE cannot be defined simultaneously. This is
49 enforced during preprocessing.
52 //#define FBSTRING_PERVERSE
53 //#define FBSTRING_CONSERVATIVE
55 #ifdef FBSTRING_PERVERSE
56 #ifdef FBSTRING_CONSERVATIVE
57 #error Cannot define both FBSTRING_PERVERSE and FBSTRING_CONSERVATIVE.
63 #include <type_traits>
65 // libc++ doesn't provide this header
66 #ifndef _LIBCPP_VERSION
67 // This file appears in two locations: inside fbcode and in the
68 // libstdc++ source code (when embedding fbstring as std::string).
69 // To aid in this schizophrenic use, two macros are defined in
71 // _LIBSTDCXX_FBSTRING - Set inside libstdc++. This is useful to
72 // gate use inside fbcode v. libstdc++
73 #include <bits/c++config.h>
76 #ifdef _LIBSTDCXX_FBSTRING
78 #pragma GCC system_header
80 // Handle the cases where the fbcode version (folly/Malloc.h) is included
81 // either before or after this inclusion.
82 #ifdef FOLLY_MALLOC_H_
83 #undef FOLLY_MALLOC_H_
84 #include "basic_fbstring_malloc.h"
86 #include "basic_fbstring_malloc.h"
87 #undef FOLLY_MALLOC_H_
90 #else // !_LIBSTDCXX_FBSTRING
96 #include "folly/Traits.h"
97 #include "folly/Malloc.h"
98 #include "folly/Hash.h"
100 #ifdef _GLIBCXX_SYMVER
101 #include <ext/hash_set>
102 #include <ext/hash_map>
107 // We defined these here rather than including Likely.h to avoid
108 // redefinition errors when fbstring is imported into libstdc++.
109 #define FBSTRING_LIKELY(x) (__builtin_expect((x), 1))
110 #define FBSTRING_UNLIKELY(x) (__builtin_expect((x), 0))
112 // Ignore shadowing warnings within this file, so includers can use -Wshadow.
113 #pragma GCC diagnostic push
114 #pragma GCC diagnostic ignored "-Wshadow"
116 // FBString cannot use throw when replacing std::string, though it may still
117 // use std::__throw_*
118 #define throw FOLLY_FBSTRING_MAY_NOT_USE_THROW
120 #ifdef _LIBSTDCXX_FBSTRING
121 namespace std _GLIBCXX_VISIBILITY(default) {
122 _GLIBCXX_BEGIN_NAMESPACE_VERSION
127 // Different versions of gcc/clang support different versions of
128 // the address sanitizer attribute. Unfortunately, this attribute
129 // has issues when inlining is used, so disable that as well.
130 #if defined(__clang__)
131 # if __has_feature(address_sanitizer)
132 # if __has_attribute(__no_address_safety_analysis__)
133 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
134 __attribute__((__no_address_safety_analysis__, __noinline__))
135 # elif __has_attribute(__no_sanitize_address__)
136 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
137 __attribute__((__no_sanitize_address__, __noinline__))
140 #elif defined (__GNUC__) && \
142 (__GNUC_MINOR__ >= 8) && \
144 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
145 __attribute__((__no_address_safety_analysis__, __noinline__))
147 #ifndef FBSTRING_DISABLE_ADDRESS_SANITIZER
148 # define FBSTRING_DISABLE_ADDRESS_SANITIZER
151 namespace fbstring_detail {
153 template <class InIt, class OutIt>
156 typename std::iterator_traits<InIt>::difference_type n,
158 for (; n != 0; --n, ++b, ++d) {
164 template <class Pod, class T>
165 inline void pod_fill(Pod* b, Pod* e, T c) {
166 assert(b && e && b <= e);
167 /*static*/ if (sizeof(T) == 1) {
170 auto const ee = b + ((e - b) & ~7u);
171 for (; b != ee; b += 8) {
182 for (; b != e; ++b) {
189 * Lightly structured memcpy, simplifies copying PODs and introduces
190 * some asserts. Unfortunately using this function may cause
191 * measurable overhead (presumably because it adjusts from a begin/end
192 * convention to a pointer/size convention, so it does some extra
193 * arithmetic even though the caller might have done the inverse
194 * adaptation outside).
197 inline void pod_copy(const Pod* b, const Pod* e, Pod* d) {
199 assert(d >= e || d + (e - b) <= b);
200 memcpy(d, b, (e - b) * sizeof(Pod));
204 * Lightly structured memmove, simplifies copying PODs and introduces
208 inline void pod_move(const Pod* b, const Pod* e, Pod* d) {
210 memmove(d, b, (e - b) * sizeof(*b));
213 } // namespace fbstring_detail
216 * Defines a special acquisition method for constructing fbstring
217 * objects. AcquireMallocatedString means that the user passes a
218 * pointer to a malloc-allocated string that the fbstring object will
221 enum class AcquireMallocatedString {};
224 * fbstring_core_model is a mock-up type that defines all required
225 * signatures of a fbstring core. The fbstring class itself uses such
226 * a core object to implement all of the numerous member functions
227 * required by the standard.
229 * If you want to define a new core, copy the definition below and
230 * implement the primitives. Then plug the core into basic_fbstring as
231 * a template argument.
233 template <class Char>
234 class fbstring_core_model {
236 fbstring_core_model();
237 fbstring_core_model(const fbstring_core_model &);
238 ~fbstring_core_model();
239 // Returns a pointer to string's buffer (currently only contiguous
240 // strings are supported). The pointer is guaranteed to be valid
241 // until the next call to a non-const member function.
242 const Char * data() const;
243 // Much like data(), except the string is prepared to support
244 // character-level changes. This call is a signal for
245 // e.g. reference-counted implementation to fork the data. The
246 // pointer is guaranteed to be valid until the next call to a
247 // non-const member function.
248 Char * mutable_data();
249 // Returns a pointer to string's buffer and guarantees that a
250 // readable '\0' lies right after the buffer. The pointer is
251 // guaranteed to be valid until the next call to a non-const member
253 const Char * c_str() const;
254 // Shrinks the string by delta characters. Asserts that delta <=
256 void shrink(size_t delta);
257 // Expands the string by delta characters (i.e. after this call
258 // size() will report the old size() plus delta) but without
259 // initializing the expanded region. Returns a pointer to the memory
260 // to be initialized (the beginning of the expanded portion). The
261 // caller is expected to fill the expanded area appropriately.
262 Char* expand_noinit(size_t delta);
263 // Expands the string by one character and sets the last character
265 void push_back(Char c);
266 // Returns the string's size.
268 // Returns the string's capacity, i.e. maximum size that the string
269 // can grow to without reallocation. Note that for reference counted
270 // strings that's technically a lie - even assigning characters
271 // within the existing size would cause a reallocation.
272 size_t capacity() const;
273 // Returns true if the data underlying the string is actually shared
274 // across multiple strings (in a refcounted fashion).
275 bool isShared() const;
276 // Makes sure that at least minCapacity characters are available for
277 // the string without reallocation. For reference-counted strings,
278 // it should fork the data even if minCapacity < size().
279 void reserve(size_t minCapacity);
282 fbstring_core_model& operator=(const fbstring_core_model &);
287 * gcc-4.7 throws what appears to be some false positive uninitialized
288 * warnings for the members of the MediumLarge struct. So, mute them here.
290 #if defined(__GNUC__) && !defined(__clang__)
291 # pragma GCC diagnostic push
292 # pragma GCC diagnostic ignored "-Wuninitialized"
296 * This is the core of the string. The code should work on 32- and
297 * 64-bit architectures and with any Char size. Porting to big endian
298 * architectures would require some changes.
300 * The storage is selected as follows (assuming we store one-byte
301 * characters on a 64-bit machine): (a) "small" strings between 0 and
302 * 23 chars are stored in-situ without allocation (the rightmost byte
303 * stores the size); (b) "medium" strings from 24 through 254 chars
304 * are stored in malloc-allocated memory that is copied eagerly; (c)
305 * "large" strings of 255 chars and above are stored in a similar
306 * structure as medium arrays, except that the string is
307 * reference-counted and copied lazily. the reference count is
308 * allocated right before the character array.
310 * The discriminator between these three strategies sits in the two
311 * most significant bits of the rightmost char of the storage. If
312 * neither is set, then the string is small (and its length sits in
313 * the lower-order bits of that rightmost character). If the MSb is
314 * set, the string is medium width. If the second MSb is set, then the
317 template <class Char> class fbstring_core {
319 fbstring_core() noexcept {
320 // Only initialize the tag, will set the MSBs (i.e. the small
321 // string size) to zero too
322 ml_.capacity_ = maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)));
323 // or: setSmallSize(0);
325 assert(category() == isSmall && size() == 0);
328 fbstring_core(const fbstring_core & rhs) {
329 assert(&rhs != this);
330 // Simplest case first: small strings are bitblitted
331 if (rhs.category() == isSmall) {
332 assert(offsetof(MediumLarge, data_) == 0);
333 assert(offsetof(MediumLarge, size_) == sizeof(ml_.data_));
334 assert(offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_));
335 const size_t size = rhs.smallSize();
337 ml_.capacity_ = rhs.ml_.capacity_;
340 // Just write the whole thing, don't look at details. In
341 // particular we need to copy capacity anyway because we want
342 // to set the size (don't forget that the last character,
343 // which stores a short string's length, is shared with the
344 // ml_.capacity field).
347 assert(category() == isSmall && this->size() == rhs.size());
348 } else if (rhs.category() == isLarge) {
349 // Large strings are just refcounted
351 RefCounted::incrementRefs(ml_.data_);
352 assert(category() == isLarge && size() == rhs.size());
354 // Medium strings are copied eagerly. Don't forget to allocate
355 // one extra Char for the null terminator.
356 auto const allocSize =
357 goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));
358 ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
359 fbstring_detail::pod_copy(rhs.ml_.data_,
361 rhs.ml_.data_ + rhs.ml_.size_ + 1,
363 // No need for writeTerminator() here, we copied one extra
364 // element just above.
365 ml_.size_ = rhs.ml_.size_;
366 ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
367 assert(category() == isMedium);
369 assert(size() == rhs.size());
370 assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
373 fbstring_core(fbstring_core&& goner) noexcept {
374 if (goner.category() == isSmall) {
375 // Just copy, leave the goner in peace
376 new(this) fbstring_core(goner.small_, goner.smallSize());
380 // Clean goner's carcass
381 goner.setSmallSize(0);
385 // NOTE(agallagher): The word-aligned copy path copies bytes which are
386 // outside the range of the string, and makes address sanitizer unhappy,
387 // so just disable it on this function.
388 fbstring_core(const Char *const data, const size_t size)
389 FBSTRING_DISABLE_ADDRESS_SANITIZER {
390 // Simplest case first: small strings are bitblitted
391 if (size <= maxSmallSize) {
392 // Layout is: Char* data_, size_t size_, size_t capacity_
393 /*static_*/assert(sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t));
394 /*static_*/assert(sizeof(Char*) == sizeof(size_t));
395 // sizeof(size_t) must be a power of 2
396 /*static_*/assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
398 // If data is aligned, use fast word-wise copying. Otherwise,
399 // use conservative memcpy.
400 if (reinterpret_cast<size_t>(data) & (sizeof(size_t) - 1)) {
401 fbstring_detail::pod_copy(data, data + size, small_);
403 // Copy one word (64 bits) at a time
404 const size_t byteSize = size * sizeof(Char);
405 if (byteSize > 2 * sizeof(size_t)) {
407 ml_.capacity_ = reinterpret_cast<const size_t*>(data)[2];
409 ml_.size_ = reinterpret_cast<const size_t*>(data)[1];
411 ml_.data_ = *reinterpret_cast<Char**>(const_cast<Char*>(data));
412 } else if (byteSize > sizeof(size_t)) {
415 } else if (size > 0) {
421 } else if (size <= maxMediumSize) {
422 // Medium strings are allocated normally. Don't forget to
423 // allocate one extra Char for the terminating null.
424 auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));
425 ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
426 fbstring_detail::pod_copy(data, data + size, ml_.data_);
428 ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
430 // Large strings are allocated differently
431 size_t effectiveCapacity = size;
432 auto const newRC = RefCounted::create(data, & effectiveCapacity);
433 ml_.data_ = newRC->data_;
435 ml_.capacity_ = effectiveCapacity | isLarge;
438 assert(this->size() == size);
439 assert(memcmp(this->data(), data, size * sizeof(Char)) == 0);
442 ~fbstring_core() noexcept {
443 auto const c = category();
451 RefCounted::decrementRefs(ml_.data_);
454 // Snatches a previously mallocated string. The parameter "size"
455 // is the size of the string, and the parameter "allocatedSize"
456 // is the size of the mallocated block. The string must be
457 // \0-terminated, so allocatedSize >= size + 1 and data[size] == '\0'.
459 // So if you want a 2-character string, pass malloc(3) as "data",
460 // pass 2 as "size", and pass 3 as "allocatedSize".
461 fbstring_core(Char * const data,
463 const size_t allocatedSize,
464 AcquireMallocatedString) {
466 assert(allocatedSize >= size + 1);
467 assert(data[size] == '\0');
468 // Use the medium string storage
471 // Don't forget about null terminator
472 ml_.capacity_ = (allocatedSize - 1) | isMedium;
474 // No need for the memory
480 // swap below doesn't test whether &rhs == this (and instead
481 // potentially does extra work) on the premise that the rarity of
482 // that situation actually makes the check more expensive than is
484 void swap(fbstring_core & rhs) {
490 // In C++11 data() and c_str() are 100% equivalent.
491 const Char * data() const {
495 Char * mutable_data() {
496 auto const c = category();
500 assert(c == isMedium || c == isLarge);
501 if (c == isLarge && RefCounted::refs(ml_.data_) > 1) {
503 size_t effectiveCapacity = ml_.capacity();
504 auto const newRC = RefCounted::create(& effectiveCapacity);
505 // If this fails, someone placed the wrong capacity in an
507 assert(effectiveCapacity >= ml_.capacity());
508 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
510 RefCounted::decrementRefs(ml_.data_);
511 ml_.data_ = newRC->data_;
512 // No need to call writeTerminator(), we have + 1 above.
517 const Char * c_str() const {
518 auto const c = category();
519 #ifdef FBSTRING_PERVERSE
521 assert(small_[smallSize()] == TERMINATOR || smallSize() == maxSmallSize
522 || small_[smallSize()] == '\0');
523 small_[smallSize()] = '\0';
526 assert(c == isMedium || c == isLarge);
527 assert(ml_.data_[ml_.size_] == TERMINATOR || ml_.data_[ml_.size_] == '\0');
528 ml_.data_[ml_.size_] = '\0';
529 #elif defined(FBSTRING_CONSERVATIVE)
531 assert(small_[smallSize()] == '\0');
534 assert(c == isMedium || c == isLarge);
535 assert(ml_.data_[ml_.size_] == '\0');
538 small_[smallSize()] = '\0';
541 assert(c == isMedium || c == isLarge);
542 ml_.data_[ml_.size_] = '\0';
547 void shrink(const size_t delta) {
548 if (category() == isSmall) {
549 // Check for underflow
550 assert(delta <= smallSize());
551 setSmallSize(smallSize() - delta);
552 } else if (category() == isMedium || RefCounted::refs(ml_.data_) == 1) {
553 // Medium strings and unique large strings need no special
555 assert(ml_.size_ >= delta);
558 assert(ml_.size_ >= delta);
559 // Shared large string, must make unique. This is because of the
560 // durn terminator must be written, which may trample the shared
563 fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this);
565 // No need to write the terminator.
571 void reserve(size_t minCapacity) {
572 if (category() == isLarge) {
574 if (RefCounted::refs(ml_.data_) > 1) {
575 // We must make it unique regardless; in-place reallocation is
576 // useless if the string is shared. In order to not surprise
577 // people, reserve the new block at current capacity or
578 // more. That way, a string's capacity never shrinks after a
580 minCapacity = std::max(minCapacity, ml_.capacity());
581 auto const newRC = RefCounted::create(& minCapacity);
582 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
584 // Done with the old data. No need to call writeTerminator(),
585 // we have + 1 above.
586 RefCounted::decrementRefs(ml_.data_);
587 ml_.data_ = newRC->data_;
588 ml_.capacity_ = minCapacity | isLarge;
589 // size remains unchanged
591 // String is not shared, so let's try to realloc (if needed)
592 if (minCapacity > ml_.capacity()) {
593 // Asking for more memory
595 RefCounted::reallocate(ml_.data_, ml_.size_,
596 ml_.capacity(), minCapacity);
597 ml_.data_ = newRC->data_;
598 ml_.capacity_ = minCapacity | isLarge;
601 assert(capacity() >= minCapacity);
603 } else if (category() == isMedium) {
604 // String is not shared
605 if (minCapacity <= ml_.capacity()) {
606 return; // nothing to do, there's enough room
608 if (minCapacity <= maxMediumSize) {
609 // Keep the string at medium size. Don't forget to allocate
610 // one extra Char for the terminating null.
611 size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char));
612 ml_.data_ = static_cast<Char *>(
615 ml_.size_ * sizeof(Char),
616 (ml_.capacity() + 1) * sizeof(Char),
619 ml_.capacity_ = (capacityBytes / sizeof(Char) - 1) | isMedium;
621 // Conversion from medium to large string
622 fbstring_core nascent;
623 // Will recurse to another branch of this function
624 nascent.reserve(minCapacity);
625 nascent.ml_.size_ = ml_.size_;
626 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_,
630 assert(capacity() >= minCapacity);
633 assert(category() == isSmall);
634 if (minCapacity > maxMediumSize) {
636 auto const newRC = RefCounted::create(& minCapacity);
637 auto const size = smallSize();
638 fbstring_detail::pod_copy(small_, small_ + size + 1, newRC->data_);
639 // No need for writeTerminator(), we wrote it above with + 1.
640 ml_.data_ = newRC->data_;
642 ml_.capacity_ = minCapacity | isLarge;
643 assert(capacity() >= minCapacity);
644 } else if (minCapacity > maxSmallSize) {
646 // Don't forget to allocate one extra Char for the terminating null
647 auto const allocSizeBytes =
648 goodMallocSize((1 + minCapacity) * sizeof(Char));
649 auto const data = static_cast<Char*>(checkedMalloc(allocSizeBytes));
650 auto const size = smallSize();
651 fbstring_detail::pod_copy(small_, small_ + size + 1, data);
652 // No need for writeTerminator(), we wrote it above with + 1.
655 ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1) | isMedium;
658 // Nothing to do, everything stays put
661 assert(capacity() >= minCapacity);
664 Char * expand_noinit(const size_t delta) {
665 // Strategy is simple: make room, then change size
666 assert(capacity() >= size());
668 if (category() == isSmall) {
671 if (newSz <= maxSmallSize) {
679 newSz = ml_.size_ + delta;
680 if (newSz > capacity()) {
684 assert(capacity() >= newSz);
685 // Category can't be small - we took care of that above
686 assert(category() == isMedium || category() == isLarge);
689 assert(size() == newSz);
690 return ml_.data_ + sz;
693 void push_back(Char c) {
694 assert(capacity() >= size());
696 if (category() == isSmall) {
698 if (sz < maxSmallSize) {
699 setSmallSize(sz + 1);
704 reserve(maxSmallSize * 2);
707 if (sz == capacity()) { // always true for isShared()
708 reserve(1 + sz * 3 / 2); // ensures not shared
712 assert(capacity() >= sz + 1);
713 // Category can't be small - we took care of that above
714 assert(category() == isMedium || category() == isLarge);
720 size_t size() const {
721 return category() == isSmall ? smallSize() : ml_.size_;
724 size_t capacity() const {
725 switch (category()) {
729 // For large-sized strings, a multi-referenced chunk has no
730 // available capacity. This is because any attempt to append
731 // data would trigger a new allocation.
732 if (RefCounted::refs(ml_.data_) > 1) return ml_.size_;
735 return ml_.capacity();
738 bool isShared() const {
739 return category() == isLarge && RefCounted::refs(ml_.data_) > 1;
742 #ifdef FBSTRING_PERVERSE
743 enum { TERMINATOR = '^' };
745 enum { TERMINATOR = '\0' };
748 void writeTerminator() {
749 #if defined(FBSTRING_PERVERSE) || defined(FBSTRING_CONSERVATIVE)
750 if (category() == isSmall) {
751 const auto s = smallSize();
752 if (s != maxSmallSize) {
753 small_[s] = TERMINATOR;
756 ml_.data_[ml_.size_] = TERMINATOR;
763 fbstring_core & operator=(const fbstring_core & rhs);
770 size_t capacity() const {
771 return capacity_ & capacityExtractMask;
776 std::atomic<size_t> refCount_;
779 static RefCounted * fromData(Char * p) {
780 return static_cast<RefCounted*>(
782 static_cast<unsigned char*>(static_cast<void*>(p))
783 - sizeof(refCount_)));
786 static size_t refs(Char * p) {
787 return fromData(p)->refCount_.load(std::memory_order_acquire);
790 static void incrementRefs(Char * p) {
791 fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel);
794 static void decrementRefs(Char * p) {
795 auto const dis = fromData(p);
796 size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel);
803 static RefCounted * create(size_t * size) {
804 // Don't forget to allocate one extra Char for the terminating
805 // null. In this case, however, one Char is already part of the
807 const size_t allocSize = goodMallocSize(
808 sizeof(RefCounted) + *size * sizeof(Char));
809 auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));
810 result->refCount_.store(1, std::memory_order_release);
811 *size = (allocSize - sizeof(RefCounted)) / sizeof(Char);
815 static RefCounted * create(const Char * data, size_t * size) {
816 const size_t effectiveSize = *size;
817 auto result = create(size);
818 fbstring_detail::pod_copy(data, data + effectiveSize, result->data_);
822 static RefCounted * reallocate(Char *const data,
823 const size_t currentSize,
824 const size_t currentCapacity,
825 const size_t newCapacity) {
826 assert(newCapacity > 0 && newCapacity > currentSize);
827 auto const dis = fromData(data);
828 assert(dis->refCount_.load(std::memory_order_acquire) == 1);
829 // Don't forget to allocate one extra Char for the terminating
830 // null. In this case, however, one Char is already part of the
832 auto result = static_cast<RefCounted*>(
834 sizeof(RefCounted) + currentSize * sizeof(Char),
835 sizeof(RefCounted) + currentCapacity * sizeof(Char),
836 sizeof(RefCounted) + newCapacity * sizeof(Char)));
837 assert(result->refCount_.load(std::memory_order_acquire) == 1);
843 mutable Char small_[sizeof(MediumLarge) / sizeof(Char)];
844 mutable MediumLarge ml_;
848 lastChar = sizeof(MediumLarge) - 1,
849 maxSmallSize = lastChar / sizeof(Char),
850 maxMediumSize = 254 / sizeof(Char), // coincides with the small
851 // bin size in dlmalloc
852 categoryExtractMask = sizeof(size_t) == 4 ? 0xC0000000 : 0xC000000000000000,
853 capacityExtractMask = ~categoryExtractMask,
855 static_assert(!(sizeof(MediumLarge) % sizeof(Char)),
856 "Corrupt memory layout for fbstring.");
860 isMedium = sizeof(size_t) == 4 ? 0x80000000 : 0x8000000000000000,
861 isLarge = sizeof(size_t) == 4 ? 0x40000000 : 0x4000000000000000,
864 Category category() const {
865 // Assumes little endian
866 return static_cast<Category>(ml_.capacity_ & categoryExtractMask);
869 size_t smallSize() const {
870 assert(category() == isSmall && small_[maxSmallSize] <= maxSmallSize);
871 return static_cast<size_t>(maxSmallSize)
872 - static_cast<size_t>(small_[maxSmallSize]);
875 void setSmallSize(size_t s) {
876 // Warning: this should work with uninitialized strings too,
877 // so don't assume anything about the previous value of
878 // small_[maxSmallSize].
879 assert(s <= maxSmallSize);
880 small_[maxSmallSize] = maxSmallSize - s;
884 #if defined(__GNUC__) && !defined(__clang__)
885 # pragma GCC diagnostic pop
888 #ifndef _LIBSTDCXX_FBSTRING
890 * Dummy fbstring core that uses an actual std::string. This doesn't
891 * make any sense - it's just for testing purposes.
893 template <class Char>
894 class dummy_fbstring_core {
896 dummy_fbstring_core() {
898 dummy_fbstring_core(const dummy_fbstring_core& another)
899 : backend_(another.backend_) {
901 dummy_fbstring_core(const Char * s, size_t n)
904 void swap(dummy_fbstring_core & rhs) {
905 backend_.swap(rhs.backend_);
907 const Char * data() const {
908 return backend_.data();
910 Char * mutable_data() {
911 //assert(!backend_.empty());
912 return &*backend_.begin();
914 void shrink(size_t delta) {
915 assert(delta <= size());
916 backend_.resize(size() - delta);
918 Char * expand_noinit(size_t delta) {
919 auto const sz = size();
920 backend_.resize(size() + delta);
921 return backend_.data() + sz;
923 void push_back(Char c) {
924 backend_.push_back(c);
926 size_t size() const {
927 return backend_.size();
929 size_t capacity() const {
930 return backend_.capacity();
932 bool isShared() const {
935 void reserve(size_t minCapacity) {
936 backend_.reserve(minCapacity);
940 std::basic_string<Char> backend_;
942 #endif // !_LIBSTDCXX_FBSTRING
945 * This is the basic_string replacement. For conformity,
946 * basic_fbstring takes the same template parameters, plus the last
947 * one which is the core.
949 #ifdef _LIBSTDCXX_FBSTRING
950 template <typename E, class T, class A, class Storage>
952 template <typename E,
953 class T = std::char_traits<E>,
954 class A = std::allocator<E>,
955 class Storage = fbstring_core<E> >
957 class basic_fbstring {
961 void (*throw_exc)(const char*),
963 if (!condition) throw_exc(msg);
966 bool isSane() const {
969 empty() == (size() == 0) &&
970 empty() == (begin() == end()) &&
971 size() <= max_size() &&
972 capacity() <= max_size() &&
973 size() <= capacity() &&
974 (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0');
978 friend struct Invariant;
981 explicit Invariant(const basic_fbstring& s) : s_(s) {
988 const basic_fbstring& s_;
990 explicit Invariant(const basic_fbstring&) {}
992 Invariant& operator=(const Invariant&);
997 typedef T traits_type;
998 typedef typename traits_type::char_type value_type;
999 typedef A allocator_type;
1000 typedef typename A::size_type size_type;
1001 typedef typename A::difference_type difference_type;
1003 typedef typename A::reference reference;
1004 typedef typename A::const_reference const_reference;
1005 typedef typename A::pointer pointer;
1006 typedef typename A::const_pointer const_pointer;
1008 typedef E* iterator;
1009 typedef const E* const_iterator;
1010 typedef std::reverse_iterator<iterator
1011 #ifdef NO_ITERATOR_TRAITS
1015 typedef std::reverse_iterator<const_iterator
1016 #ifdef NO_ITERATOR_TRAITS
1019 > const_reverse_iterator;
1021 static const size_type npos; // = size_type(-1)
1024 static void procrustes(size_type& n, size_type nmax) {
1025 if (n > nmax) n = nmax;
1029 // C++11 21.4.2 construct/copy/destroy
1030 explicit basic_fbstring(const A& a = A()) noexcept {
1033 basic_fbstring(const basic_fbstring& str)
1034 : store_(str.store_) {
1038 basic_fbstring(basic_fbstring&& goner) noexcept
1039 : store_(std::move(goner.store_)) {
1042 #ifndef _LIBSTDCXX_FBSTRING
1043 // This is defined for compatibility with std::string
1044 /* implicit */ basic_fbstring(const std::string& str)
1045 : store_(str.data(), str.size()) {
1049 basic_fbstring(const basic_fbstring& str, size_type pos,
1050 size_type n = npos, const A& a = A()) {
1051 assign(str, pos, n);
1054 /* implicit */ basic_fbstring(const value_type* s, const A& a = A())
1055 : store_(s, s ? traits_type::length(s) : ({
1056 basic_fbstring<char> err = __PRETTY_FUNCTION__;
1057 err += ": null pointer initializer not valid";
1058 std::__throw_logic_error(err.c_str());
1063 basic_fbstring(const value_type* s, size_type n, const A& a = A())
1067 basic_fbstring(size_type n, value_type c, const A& a = A()) {
1068 auto const data = store_.expand_noinit(n);
1069 fbstring_detail::pod_fill(data, data + n, c);
1070 store_.writeTerminator();
1073 template <class InIt>
1074 basic_fbstring(InIt begin, InIt end,
1075 typename std::enable_if<
1076 !std::is_same<typename std::remove_const<InIt>::type,
1077 value_type*>::value, const A>::type & a = A()) {
1081 // Specialization for const char*, const char*
1082 basic_fbstring(const value_type* b, const value_type* e)
1083 : store_(b, e - b) {
1086 // Nonstandard constructor
1087 basic_fbstring(value_type *s, size_type n, size_type c,
1088 AcquireMallocatedString a)
1089 : store_(s, n, c, a) {
1092 // Construction from initialization list
1093 basic_fbstring(std::initializer_list<value_type> il) {
1094 assign(il.begin(), il.end());
1097 ~basic_fbstring() noexcept {
1100 basic_fbstring& operator=(const basic_fbstring& lhs) {
1101 if (FBSTRING_UNLIKELY(&lhs == this)) {
1104 auto const oldSize = size();
1105 auto const srcSize = lhs.size();
1106 if (capacity() >= srcSize && !store_.isShared()) {
1107 // great, just copy the contents
1108 if (oldSize < srcSize)
1109 store_.expand_noinit(srcSize - oldSize);
1111 store_.shrink(oldSize - srcSize);
1112 assert(size() == srcSize);
1113 fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin());
1114 store_.writeTerminator();
1116 // need to reallocate, so we may as well create a brand new string
1117 basic_fbstring(lhs).swap(*this);
1123 basic_fbstring& operator=(basic_fbstring&& goner) noexcept {
1124 if (FBSTRING_UNLIKELY(&goner == this)) {
1125 // Compatibility with std::basic_string<>,
1126 // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.
1129 // No need of this anymore
1130 this->~basic_fbstring();
1131 // Move the goner into this
1132 new(&store_) fbstring_core<E>(std::move(goner.store_));
1136 #ifndef _LIBSTDCXX_FBSTRING
1137 // Compatibility with std::string
1138 basic_fbstring & operator=(const std::string & rhs) {
1139 return assign(rhs.data(), rhs.size());
1142 // Compatibility with std::string
1143 std::string toStdString() const {
1144 return std::string(data(), size());
1147 // A lot of code in fbcode still uses this method, so keep it here for now.
1148 const basic_fbstring& toStdString() const {
1153 basic_fbstring& operator=(const value_type* s) {
1157 basic_fbstring& operator=(value_type c) {
1159 store_.expand_noinit(1);
1160 } else if (store_.isShared()) {
1161 basic_fbstring(1, c).swap(*this);
1164 store_.shrink(size() - 1);
1166 *store_.mutable_data() = c;
1167 store_.writeTerminator();
1171 basic_fbstring& operator=(std::initializer_list<value_type> il) {
1172 return assign(il.begin(), il.end());
1175 // C++11 21.4.3 iterators:
1176 iterator begin() { return store_.mutable_data(); }
1178 const_iterator begin() const { return store_.data(); }
1180 const_iterator cbegin() const { return begin(); }
1183 return store_.mutable_data() + store_.size();
1186 const_iterator end() const {
1187 return store_.data() + store_.size();
1190 const_iterator cend() const { return end(); }
1192 reverse_iterator rbegin() {
1193 return reverse_iterator(end());
1196 const_reverse_iterator rbegin() const {
1197 return const_reverse_iterator(end());
1200 const_reverse_iterator crbegin() const { return rbegin(); }
1202 reverse_iterator rend() {
1203 return reverse_iterator(begin());
1206 const_reverse_iterator rend() const {
1207 return const_reverse_iterator(begin());
1210 const_reverse_iterator crend() const { return rend(); }
1213 // C++11 21.4.5, element access:
1214 const value_type& front() const { return *begin(); }
1215 const value_type& back() const {
1217 // Should be begin()[size() - 1], but that branches twice
1218 return *(end() - 1);
1220 value_type& front() { return *begin(); }
1221 value_type& back() {
1223 // Should be begin()[size() - 1], but that branches twice
1224 return *(end() - 1);
1231 // C++11 21.4.4 capacity:
1232 size_type size() const { return store_.size(); }
1234 size_type length() const { return size(); }
1236 size_type max_size() const {
1237 return std::numeric_limits<size_type>::max();
1240 void resize(const size_type n, const value_type c = value_type()) {
1241 auto size = this->size();
1243 store_.shrink(size - n);
1245 // Do this in two steps to minimize slack memory copied (see
1247 auto const capacity = this->capacity();
1248 assert(capacity >= size);
1249 if (size < capacity) {
1250 auto delta = std::min(n, capacity) - size;
1251 store_.expand_noinit(delta);
1252 fbstring_detail::pod_fill(begin() + size, end(), c);
1255 store_.writeTerminator();
1260 auto const delta = n - size;
1261 store_.expand_noinit(delta);
1262 fbstring_detail::pod_fill(end() - delta, end(), c);
1263 store_.writeTerminator();
1265 assert(this->size() == n);
1268 size_type capacity() const { return store_.capacity(); }
1270 void reserve(size_type res_arg = 0) {
1271 enforce(res_arg <= max_size(), std::__throw_length_error, "");
1272 store_.reserve(res_arg);
1275 void shrink_to_fit() {
1276 // Shrink only if slack memory is sufficiently large
1277 if (capacity() < size() * 3 / 2) {
1280 basic_fbstring(cbegin(), cend()).swap(*this);
1283 void clear() { resize(0); }
1285 bool empty() const { return size() == 0; }
1287 // C++11 21.4.5 element access:
1288 const_reference operator[](size_type pos) const {
1289 return *(c_str() + pos);
1292 reference operator[](size_type pos) {
1293 if (pos == size()) {
1294 // Just call c_str() to make sure '\0' is present
1297 return *(begin() + pos);
1300 const_reference at(size_type n) const {
1301 enforce(n <= size(), std::__throw_out_of_range, "");
1305 reference at(size_type n) {
1306 enforce(n < size(), std::__throw_out_of_range, "");
1310 // C++11 21.4.6 modifiers:
1311 basic_fbstring& operator+=(const basic_fbstring& str) {
1315 basic_fbstring& operator+=(const value_type* s) {
1319 basic_fbstring& operator+=(const value_type c) {
1324 basic_fbstring& operator+=(std::initializer_list<value_type> il) {
1329 basic_fbstring& append(const basic_fbstring& str) {
1331 auto desiredSize = size() + str.size();
1333 append(str.data(), str.size());
1334 assert(size() == desiredSize);
1338 basic_fbstring& append(const basic_fbstring& str, const size_type pos,
1340 const size_type sz = str.size();
1341 enforce(pos <= sz, std::__throw_out_of_range, "");
1342 procrustes(n, sz - pos);
1343 return append(str.data() + pos, n);
1346 basic_fbstring& append(const value_type* s, size_type n) {
1348 Invariant checker(*this);
1351 if (FBSTRING_UNLIKELY(!n)) {
1352 // Unlikely but must be done
1355 auto const oldSize = size();
1356 auto const oldData = data();
1357 // Check for aliasing (rare). We could use "<=" here but in theory
1358 // those do not work for pointers unless the pointers point to
1359 // elements in the same array. For that reason we use
1360 // std::less_equal, which is guaranteed to offer a total order
1361 // over pointers. See discussion at http://goo.gl/Cy2ya for more
1363 std::less_equal<const value_type*> le;
1364 if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
1365 assert(le(s + n, oldData + oldSize));
1366 const size_type offset = s - oldData;
1367 store_.reserve(oldSize + n);
1368 // Restore the source
1369 s = data() + offset;
1371 // Warning! Repeated appends with short strings may actually incur
1372 // practically quadratic performance. Avoid that by pushing back
1373 // the first character (which ensures exponential growth) and then
1374 // appending the rest normally. Worst case the append may incur a
1375 // second allocation but that will be rare.
1378 memcpy(store_.expand_noinit(n), s, n * sizeof(value_type));
1379 assert(size() == oldSize + n + 1);
1383 basic_fbstring& append(const value_type* s) {
1384 return append(s, traits_type::length(s));
1387 basic_fbstring& append(size_type n, value_type c) {
1388 resize(size() + n, c);
1392 template<class InputIterator>
1393 basic_fbstring& append(InputIterator first, InputIterator last) {
1394 insert(end(), first, last);
1398 basic_fbstring& append(std::initializer_list<value_type> il) {
1399 return append(il.begin(), il.end());
1402 void push_back(const value_type c) { // primitive
1403 store_.push_back(c);
1406 basic_fbstring& assign(const basic_fbstring& str) {
1407 if (&str == this) return *this;
1408 return assign(str.data(), str.size());
1411 basic_fbstring& assign(basic_fbstring&& str) {
1412 return *this = std::move(str);
1415 basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
1417 const size_type sz = str.size();
1418 enforce(pos <= sz, std::__throw_out_of_range, "");
1419 procrustes(n, sz - pos);
1420 return assign(str.data() + pos, n);
1423 basic_fbstring& assign(const value_type* s, const size_type n) {
1424 Invariant checker(*this);
1427 std::copy(s, s + n, begin());
1429 assert(size() == n);
1431 const value_type *const s2 = s + size();
1432 std::copy(s, s2, begin());
1433 append(s2, n - size());
1434 assert(size() == n);
1436 store_.writeTerminator();
1437 assert(size() == n);
1441 basic_fbstring& assign(const value_type* s) {
1442 return assign(s, traits_type::length(s));
1445 basic_fbstring& assign(std::initializer_list<value_type> il) {
1446 return assign(il.begin(), il.end());
1449 template <class ItOrLength, class ItOrChar>
1450 basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {
1451 return replace(begin(), end(), first_or_n, last_or_c);
1454 basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {
1455 return insert(pos1, str.data(), str.size());
1458 basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
1459 size_type pos2, size_type n) {
1460 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1461 procrustes(n, str.length() - pos2);
1462 return insert(pos1, str.data() + pos2, n);
1465 basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {
1466 enforce(pos <= length(), std::__throw_out_of_range, "");
1467 insert(begin() + pos, s, s + n);
1471 basic_fbstring& insert(size_type pos, const value_type* s) {
1472 return insert(pos, s, traits_type::length(s));
1475 basic_fbstring& insert(size_type pos, size_type n, value_type c) {
1476 enforce(pos <= length(), std::__throw_out_of_range, "");
1477 insert(begin() + pos, n, c);
1481 iterator insert(const_iterator p, const value_type c) {
1482 const size_type pos = p - begin();
1484 return begin() + pos;
1488 template <int i> class Selector {};
1490 iterator insertImplDiscr(const_iterator p,
1491 size_type n, value_type c, Selector<1>) {
1492 Invariant checker(*this);
1494 auto const pos = p - begin();
1495 assert(p >= begin() && p <= end());
1496 if (capacity() - size() < n) {
1497 const size_type sz = p - begin();
1498 reserve(size() + n);
1501 const iterator oldEnd = end();
1502 if (n < size_type(oldEnd - p)) {
1503 append(oldEnd - n, oldEnd);
1505 // reverse_iterator(oldEnd - n),
1506 // reverse_iterator(p),
1507 // reverse_iterator(oldEnd));
1508 fbstring_detail::pod_move(&*p, &*oldEnd - n,
1510 std::fill(begin() + pos, begin() + pos + n, c);
1512 append(n - (end() - p), c);
1513 append(iterator(p), oldEnd);
1514 std::fill(iterator(p), oldEnd, c);
1516 store_.writeTerminator();
1517 return begin() + pos;
1520 template<class InputIter>
1521 iterator insertImplDiscr(const_iterator i,
1522 InputIter b, InputIter e, Selector<0>) {
1523 return insertImpl(i, b, e,
1524 typename std::iterator_traits<InputIter>::iterator_category());
1527 template <class FwdIterator>
1528 iterator insertImpl(const_iterator i,
1529 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1530 Invariant checker(*this);
1532 const size_type pos = i - begin();
1533 const typename std::iterator_traits<FwdIterator>::difference_type n2 =
1534 std::distance(s1, s2);
1536 using namespace fbstring_detail;
1537 assert(pos <= size());
1539 const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
1540 capacity() - size();
1542 // realloc the string
1543 reserve(size() + n2);
1546 if (pos + n2 <= size()) {
1547 const iterator tailBegin = end() - n2;
1548 store_.expand_noinit(n2);
1549 fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2);
1550 std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i),
1551 reverse_iterator(tailBegin + n2));
1552 std::copy(s1, s2, begin() + pos);
1555 const size_type old_size = size();
1556 std::advance(t, old_size - pos);
1557 const size_t newElems = std::distance(t, s2);
1558 store_.expand_noinit(n2);
1559 std::copy(t, s2, begin() + old_size);
1560 fbstring_detail::pod_copy(data() + pos, data() + old_size,
1561 begin() + old_size + newElems);
1562 std::copy(s1, t, begin() + pos);
1564 store_.writeTerminator();
1565 return begin() + pos;
1568 template <class InputIterator>
1569 iterator insertImpl(const_iterator i,
1570 InputIterator b, InputIterator e,
1571 std::input_iterator_tag) {
1572 const auto pos = i - begin();
1573 basic_fbstring temp(begin(), i);
1574 for (; b != e; ++b) {
1577 temp.append(i, cend());
1579 return begin() + pos;
1583 template <class ItOrLength, class ItOrChar>
1584 iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
1585 Selector<std::numeric_limits<ItOrLength>::is_specialized> sel;
1586 return insertImplDiscr(p, first_or_n, last_or_c, sel);
1589 iterator insert(const_iterator p, std::initializer_list<value_type> il) {
1590 return insert(p, il.begin(), il.end());
1593 basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
1594 Invariant checker(*this);
1596 enforce(pos <= length(), std::__throw_out_of_range, "");
1597 procrustes(n, length() - pos);
1598 std::copy(begin() + pos + n, end(), begin() + pos);
1599 resize(length() - n);
1603 iterator erase(iterator position) {
1604 const size_type pos(position - begin());
1605 enforce(pos <= size(), std::__throw_out_of_range, "");
1607 return begin() + pos;
1610 iterator erase(iterator first, iterator last) {
1611 const size_type pos(first - begin());
1612 erase(pos, last - first);
1613 return begin() + pos;
1616 // Replaces at most n1 chars of *this, starting with pos1 with the
1618 basic_fbstring& replace(size_type pos1, size_type n1,
1619 const basic_fbstring& str) {
1620 return replace(pos1, n1, str.data(), str.size());
1623 // Replaces at most n1 chars of *this, starting with pos1,
1624 // with at most n2 chars of str starting with pos2
1625 basic_fbstring& replace(size_type pos1, size_type n1,
1626 const basic_fbstring& str,
1627 size_type pos2, size_type n2) {
1628 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1629 return replace(pos1, n1, str.data() + pos2,
1630 std::min(n2, str.size() - pos2));
1633 // Replaces at most n1 chars of *this, starting with pos, with chars from s
1634 basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
1635 return replace(pos, n1, s, traits_type::length(s));
1638 // Replaces at most n1 chars of *this, starting with pos, with n2
1641 // consolidated with
1643 // Replaces at most n1 chars of *this, starting with pos, with at
1644 // most n2 chars of str. str must have at least n2 chars.
1645 template <class StrOrLength, class NumOrChar>
1646 basic_fbstring& replace(size_type pos, size_type n1,
1647 StrOrLength s_or_n2, NumOrChar n_or_c) {
1648 Invariant checker(*this);
1650 enforce(pos <= size(), std::__throw_out_of_range, "");
1651 procrustes(n1, length() - pos);
1652 const iterator b = begin() + pos;
1653 return replace(b, b + n1, s_or_n2, n_or_c);
1656 basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {
1657 return replace(i1, i2, str.data(), str.length());
1660 basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
1661 return replace(i1, i2, s, traits_type::length(s));
1665 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1666 const value_type* s, size_type n,
1669 assert(begin() <= i1 && i1 <= end());
1670 assert(begin() <= i2 && i2 <= end());
1671 return replace(i1, i2, s, s + n);
1674 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1675 size_type n2, value_type c, Selector<1>) {
1676 const size_type n1 = i2 - i1;
1678 std::fill(i1, i1 + n2, c);
1681 std::fill(i1, i2, c);
1682 insert(i2, n2 - n1, c);
1688 template <class InputIter>
1689 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1690 InputIter b, InputIter e,
1692 replaceImpl(i1, i2, b, e,
1693 typename std::iterator_traits<InputIter>::iterator_category());
1698 template <class FwdIterator>
1699 bool replaceAliased(iterator i1, iterator i2,
1700 FwdIterator s1, FwdIterator s2, std::false_type) {
1704 template <class FwdIterator>
1705 bool replaceAliased(iterator i1, iterator i2,
1706 FwdIterator s1, FwdIterator s2, std::true_type) {
1707 static const std::less_equal<const value_type*> le =
1708 std::less_equal<const value_type*>();
1709 const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());
1713 // Aliased replace, copy to new string
1714 basic_fbstring temp;
1715 temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));
1716 temp.append(begin(), i1).append(s1, s2).append(i2, end());
1721 template <class FwdIterator>
1722 void replaceImpl(iterator i1, iterator i2,
1723 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1724 Invariant checker(*this);
1727 // Handle aliased replace
1728 if (replaceAliased(i1, i2, s1, s2,
1729 std::integral_constant<bool,
1730 std::is_same<FwdIterator, iterator>::value ||
1731 std::is_same<FwdIterator, const_iterator>::value>())) {
1735 auto const n1 = i2 - i1;
1737 auto const n2 = std::distance(s1, s2);
1742 std::copy(s1, s2, i1);
1746 fbstring_detail::copy_n(s1, n1, i1);
1747 std::advance(s1, n1);
1753 template <class InputIterator>
1754 void replaceImpl(iterator i1, iterator i2,
1755 InputIterator b, InputIterator e, std::input_iterator_tag) {
1756 basic_fbstring temp(begin(), i1);
1757 temp.append(b, e).append(i2, end());
1762 template <class T1, class T2>
1763 basic_fbstring& replace(iterator i1, iterator i2,
1764 T1 first_or_n_or_s, T2 last_or_c_or_n) {
1766 num1 = std::numeric_limits<T1>::is_specialized,
1767 num2 = std::numeric_limits<T2>::is_specialized;
1768 return replaceImplDiscr(
1769 i1, i2, first_or_n_or_s, last_or_c_or_n,
1770 Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>());
1773 size_type copy(value_type* s, size_type n, size_type pos = 0) const {
1774 enforce(pos <= size(), std::__throw_out_of_range, "");
1775 procrustes(n, size() - pos);
1777 fbstring_detail::pod_copy(
1784 void swap(basic_fbstring& rhs) {
1785 store_.swap(rhs.store_);
1788 const value_type* c_str() const {
1789 return store_.c_str();
1792 const value_type* data() const { return c_str(); }
1794 allocator_type get_allocator() const {
1795 return allocator_type();
1798 size_type find(const basic_fbstring& str, size_type pos = 0) const {
1799 return find(str.data(), pos, str.length());
1802 size_type find(const value_type* needle, const size_type pos,
1803 const size_type nsize) const {
1804 if (!nsize) return pos;
1805 auto const size = this->size();
1806 // nsize + pos can overflow (eg pos == npos), guard against that by checking
1807 // that nsize + pos does not wrap around.
1808 if (nsize + pos > size || nsize + pos < pos) return npos;
1809 // Don't use std::search, use a Boyer-Moore-like trick by comparing
1810 // the last characters first
1811 auto const haystack = data();
1812 auto const nsize_1 = nsize - 1;
1813 auto const lastNeedle = needle[nsize_1];
1815 // Boyer-Moore skip value for the last char in the needle. Zero is
1816 // not a valid value; skip will be computed the first time it's
1820 const E * i = haystack + pos;
1821 auto iEnd = haystack + size - nsize_1;
1824 // Boyer-Moore: match the last element in the needle
1825 while (i[nsize_1] != lastNeedle) {
1831 // Here we know that the last char matches
1832 // Continue in pedestrian mode
1833 for (size_t j = 0; ; ) {
1835 if (i[j] != needle[j]) {
1836 // Not found, we can skip
1837 // Compute the skip value lazily
1840 while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {
1847 // Check if done searching
1850 return i - haystack;
1857 size_type find(const value_type* s, size_type pos = 0) const {
1858 return find(s, pos, traits_type::length(s));
1861 size_type find (value_type c, size_type pos = 0) const {
1862 return find(&c, pos, 1);
1865 size_type rfind(const basic_fbstring& str, size_type pos = npos) const {
1866 return rfind(str.data(), pos, str.length());
1869 size_type rfind(const value_type* s, size_type pos, size_type n) const {
1870 if (n > length()) return npos;
1871 pos = std::min(pos, length() - n);
1872 if (n == 0) return pos;
1874 const_iterator i(begin() + pos);
1876 if (traits_type::eq(*i, *s)
1877 && traits_type::compare(&*i, s, n) == 0) {
1880 if (i == begin()) break;
1885 size_type rfind(const value_type* s, size_type pos = npos) const {
1886 return rfind(s, pos, traits_type::length(s));
1889 size_type rfind(value_type c, size_type pos = npos) const {
1890 return rfind(&c, pos, 1);
1893 size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {
1894 return find_first_of(str.data(), pos, str.length());
1897 size_type find_first_of(const value_type* s,
1898 size_type pos, size_type n) const {
1899 if (pos > length() || n == 0) return npos;
1900 const_iterator i(begin() + pos),
1902 for (; i != finish; ++i) {
1903 if (traits_type::find(s, n, *i) != 0) {
1910 size_type find_first_of(const value_type* s, size_type pos = 0) const {
1911 return find_first_of(s, pos, traits_type::length(s));
1914 size_type find_first_of(value_type c, size_type pos = 0) const {
1915 return find_first_of(&c, pos, 1);
1918 size_type find_last_of (const basic_fbstring& str,
1919 size_type pos = npos) const {
1920 return find_last_of(str.data(), pos, str.length());
1923 size_type find_last_of (const value_type* s, size_type pos,
1924 size_type n) const {
1925 if (!empty() && n > 0) {
1926 pos = std::min(pos, length() - 1);
1927 const_iterator i(begin() + pos);
1929 if (traits_type::find(s, n, *i) != 0) {
1932 if (i == begin()) break;
1938 size_type find_last_of (const value_type* s,
1939 size_type pos = npos) const {
1940 return find_last_of(s, pos, traits_type::length(s));
1943 size_type find_last_of (value_type c, size_type pos = npos) const {
1944 return find_last_of(&c, pos, 1);
1947 size_type find_first_not_of(const basic_fbstring& str,
1948 size_type pos = 0) const {
1949 return find_first_not_of(str.data(), pos, str.size());
1952 size_type find_first_not_of(const value_type* s, size_type pos,
1953 size_type n) const {
1954 if (pos < length()) {
1958 for (; i != finish; ++i) {
1959 if (traits_type::find(s, n, *i) == 0) {
1967 size_type find_first_not_of(const value_type* s,
1968 size_type pos = 0) const {
1969 return find_first_not_of(s, pos, traits_type::length(s));
1972 size_type find_first_not_of(value_type c, size_type pos = 0) const {
1973 return find_first_not_of(&c, pos, 1);
1976 size_type find_last_not_of(const basic_fbstring& str,
1977 size_type pos = npos) const {
1978 return find_last_not_of(str.data(), pos, str.length());
1981 size_type find_last_not_of(const value_type* s, size_type pos,
1982 size_type n) const {
1983 if (!this->empty()) {
1984 pos = std::min(pos, size() - 1);
1985 const_iterator i(begin() + pos);
1987 if (traits_type::find(s, n, *i) == 0) {
1990 if (i == begin()) break;
1996 size_type find_last_not_of(const value_type* s,
1997 size_type pos = npos) const {
1998 return find_last_not_of(s, pos, traits_type::length(s));
2001 size_type find_last_not_of (value_type c, size_type pos = npos) const {
2002 return find_last_not_of(&c, pos, 1);
2005 basic_fbstring substr(size_type pos = 0, size_type n = npos) const {
2006 enforce(pos <= size(), std::__throw_out_of_range, "");
2007 return basic_fbstring(data() + pos, std::min(n, size() - pos));
2010 int compare(const basic_fbstring& str) const {
2011 // FIX due to Goncalo N M de Carvalho July 18, 2005
2012 return compare(0, size(), str);
2015 int compare(size_type pos1, size_type n1,
2016 const basic_fbstring& str) const {
2017 return compare(pos1, n1, str.data(), str.size());
2020 int compare(size_type pos1, size_type n1,
2021 const value_type* s) const {
2022 return compare(pos1, n1, s, traits_type::length(s));
2025 int compare(size_type pos1, size_type n1,
2026 const value_type* s, size_type n2) const {
2027 enforce(pos1 <= size(), std::__throw_out_of_range, "");
2028 procrustes(n1, size() - pos1);
2029 // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!
2030 const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));
2031 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2034 int compare(size_type pos1, size_type n1,
2035 const basic_fbstring& str,
2036 size_type pos2, size_type n2) const {
2037 enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
2038 return compare(pos1, n1, str.data() + pos2,
2039 std::min(n2, str.size() - pos2));
2042 // Code from Jean-Francois Bastien (03/26/2007)
2043 int compare(const value_type* s) const {
2044 // Could forward to compare(0, size(), s, traits_type::length(s))
2045 // but that does two extra checks
2046 const size_type n1(size()), n2(traits_type::length(s));
2047 const int r = traits_type::compare(data(), s, std::min(n1, n2));
2048 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2056 // non-member functions
2058 template <typename E, class T, class A, class S>
2060 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2061 const basic_fbstring<E, T, A, S>& rhs) {
2063 basic_fbstring<E, T, A, S> result;
2064 result.reserve(lhs.size() + rhs.size());
2065 result.append(lhs).append(rhs);
2066 return std::move(result);
2070 template <typename E, class T, class A, class S>
2072 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2073 const basic_fbstring<E, T, A, S>& rhs) {
2074 return std::move(lhs.append(rhs));
2078 template <typename E, class T, class A, class S>
2080 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2081 basic_fbstring<E, T, A, S>&& rhs) {
2082 if (rhs.capacity() >= lhs.size() + rhs.size()) {
2083 // Good, at least we don't need to reallocate
2084 return std::move(rhs.insert(0, lhs));
2086 // Meh, no go. Forward to operator+(const&, const&).
2087 auto const& rhsC = rhs;
2092 template <typename E, class T, class A, class S>
2094 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2095 basic_fbstring<E, T, A, S>&& rhs) {
2096 return std::move(lhs.append(rhs));
2099 template <typename E, class T, class A, class S>
2101 basic_fbstring<E, T, A, S> operator+(
2102 const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2103 const basic_fbstring<E, T, A, S>& rhs) {
2105 basic_fbstring<E, T, A, S> result;
2106 const typename basic_fbstring<E, T, A, S>::size_type len =
2107 basic_fbstring<E, T, A, S>::traits_type::length(lhs);
2108 result.reserve(len + rhs.size());
2109 result.append(lhs, len).append(rhs);
2113 template <typename E, class T, class A, class S>
2115 basic_fbstring<E, T, A, S> operator+(
2116 typename basic_fbstring<E, T, A, S>::value_type lhs,
2117 const basic_fbstring<E, T, A, S>& rhs) {
2119 basic_fbstring<E, T, A, S> result;
2120 result.reserve(1 + rhs.size());
2121 result.push_back(lhs);
2126 template <typename E, class T, class A, class S>
2128 basic_fbstring<E, T, A, S> operator+(
2129 const basic_fbstring<E, T, A, S>& lhs,
2130 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2132 typedef typename basic_fbstring<E, T, A, S>::size_type size_type;
2133 typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;
2135 basic_fbstring<E, T, A, S> result;
2136 const size_type len = traits_type::length(rhs);
2137 result.reserve(lhs.size() + len);
2138 result.append(lhs).append(rhs, len);
2142 template <typename E, class T, class A, class S>
2144 basic_fbstring<E, T, A, S> operator+(
2145 const basic_fbstring<E, T, A, S>& lhs,
2146 typename basic_fbstring<E, T, A, S>::value_type rhs) {
2148 basic_fbstring<E, T, A, S> result;
2149 result.reserve(lhs.size() + 1);
2151 result.push_back(rhs);
2155 template <typename E, class T, class A, class S>
2157 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2158 const basic_fbstring<E, T, A, S>& rhs) {
2159 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; }
2161 template <typename E, class T, class A, class S>
2163 bool operator==(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2164 const basic_fbstring<E, T, A, S>& rhs) {
2165 return rhs == lhs; }
2167 template <typename E, class T, class A, class S>
2169 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2170 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2171 return lhs.compare(rhs) == 0; }
2173 template <typename E, class T, class A, class S>
2175 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2176 const basic_fbstring<E, T, A, S>& rhs) {
2177 return !(lhs == rhs); }
2179 template <typename E, class T, class A, class S>
2181 bool operator!=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2182 const basic_fbstring<E, T, A, S>& rhs) {
2183 return !(lhs == rhs); }
2185 template <typename E, class T, class A, class S>
2187 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2188 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2189 return !(lhs == rhs); }
2191 template <typename E, class T, class A, class S>
2193 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2194 const basic_fbstring<E, T, A, S>& rhs) {
2195 return lhs.compare(rhs) < 0; }
2197 template <typename E, class T, class A, class S>
2199 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2200 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2201 return lhs.compare(rhs) < 0; }
2203 template <typename E, class T, class A, class S>
2205 bool operator<(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2206 const basic_fbstring<E, T, A, S>& rhs) {
2207 return rhs.compare(lhs) > 0; }
2209 template <typename E, class T, class A, class S>
2211 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2212 const basic_fbstring<E, T, A, S>& rhs) {
2215 template <typename E, class T, class A, class S>
2217 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2218 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2221 template <typename E, class T, class A, class S>
2223 bool operator>(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2224 const basic_fbstring<E, T, A, S>& rhs) {
2227 template <typename E, class T, class A, class S>
2229 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2230 const basic_fbstring<E, T, A, S>& rhs) {
2231 return !(rhs < lhs); }
2233 template <typename E, class T, class A, class S>
2235 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2236 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2237 return !(rhs < lhs); }
2239 template <typename E, class T, class A, class S>
2241 bool operator<=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2242 const basic_fbstring<E, T, A, S>& rhs) {
2243 return !(rhs < lhs); }
2245 template <typename E, class T, class A, class S>
2247 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2248 const basic_fbstring<E, T, A, S>& rhs) {
2249 return !(lhs < rhs); }
2251 template <typename E, class T, class A, class S>
2253 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2254 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2255 return !(lhs < rhs); }
2257 template <typename E, class T, class A, class S>
2259 bool operator>=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2260 const basic_fbstring<E, T, A, S>& rhs) {
2261 return !(lhs < rhs);
2265 template <typename E, class T, class A, class S>
2266 void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {
2270 // TODO: make this faster.
2271 template <typename E, class T, class A, class S>
2274 typename basic_fbstring<E, T, A, S>::value_type,
2275 typename basic_fbstring<E, T, A, S>::traits_type>&
2277 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2278 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2279 basic_fbstring<E, T, A, S>& str) {
2280 typename std::basic_istream<E, T>::sentry sentry(is);
2281 typedef std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2282 typename basic_fbstring<E, T, A, S>::traits_type>
2284 typedef typename __istream_type::ios_base __ios_base;
2285 size_t extracted = 0;
2286 auto err = __ios_base::goodbit;
2288 auto n = is.width();
2293 auto got = is.rdbuf()->sgetc();
2294 for (; extracted != n && got != T::eof() && !isspace(got); ++extracted) {
2295 // Whew. We get to store this guy
2297 got = is.rdbuf()->snextc();
2299 if (got == T::eof()) {
2300 err |= __ios_base::eofbit;
2305 err |= __ios_base::failbit;
2313 template <typename E, class T, class A, class S>
2315 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2316 typename basic_fbstring<E, T, A, S>::traits_type>&
2318 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2319 typename basic_fbstring<E, T, A, S>::traits_type>& os,
2320 const basic_fbstring<E, T, A, S>& str) {
2322 typename std::basic_ostream<
2323 typename basic_fbstring<E, T, A, S>::value_type,
2324 typename basic_fbstring<E, T, A, S>::traits_type>::sentry __s(os);
2326 typedef std::ostreambuf_iterator<
2327 typename basic_fbstring<E, T, A, S>::value_type,
2328 typename basic_fbstring<E, T, A, S>::traits_type> _Ip;
2329 size_t __len = str.size();
2331 (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
2332 if (__pad_and_output(_Ip(os),
2334 __left ? str.data() + __len : str.data(),
2337 os.fill()).failed()) {
2338 os.setstate(std::ios_base::badbit | std::ios_base::failbit);
2342 std::__ostream_insert(os, str.data(), str.size());
2347 #ifndef _LIBSTDCXX_FBSTRING
2349 template <typename E, class T, class A, class S>
2351 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2352 typename basic_fbstring<E, T, A, S>::traits_type>&
2354 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2355 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2356 basic_fbstring<E, T, A, S>& str,
2357 typename basic_fbstring<E, T, A, S>::value_type delim) {
2358 // Use the nonstandard getdelim()
2362 // This looks quadratic but it really depends on realloc
2363 auto const newSize = size + 128;
2364 buf = static_cast<char*>(checkedRealloc(buf, newSize));
2365 is.getline(buf + size, newSize - size, delim);
2366 if (is.bad() || is.eof() || !is.fail()) {
2367 // done by either failure, end of file, or normal read
2368 size += std::strlen(buf + size);
2371 // Here we have failed due to too short a buffer
2372 // Minus one to discount the terminating '\0'
2374 assert(buf[size] == 0);
2375 // Clear the error so we can continue reading
2378 basic_fbstring<E, T, A, S> result(buf, size, size + 1,
2379 AcquireMallocatedString());
2384 template <typename E, class T, class A, class S>
2386 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2387 typename basic_fbstring<E, T, A, S>::traits_type>&
2389 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2390 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2391 basic_fbstring<E, T, A, S>& str) {
2392 // Just forward to the version with a delimiter
2393 return getline(is, str, '\n');
2398 template <typename E1, class T, class A, class S>
2399 const typename basic_fbstring<E1, T, A, S>::size_type
2400 basic_fbstring<E1, T, A, S>::npos =
2401 static_cast<typename basic_fbstring<E1, T, A, S>::size_type>(-1);
2403 #ifndef _LIBSTDCXX_FBSTRING
2404 // basic_string compatibility routines
2406 template <typename E, class T, class A, class S>
2408 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2409 const std::string& rhs) {
2410 return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;
2413 template <typename E, class T, class A, class S>
2415 bool operator==(const std::string& lhs,
2416 const basic_fbstring<E, T, A, S>& rhs) {
2420 template <typename E, class T, class A, class S>
2422 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2423 const std::string& rhs) {
2424 return !(lhs == rhs);
2427 template <typename E, class T, class A, class S>
2429 bool operator!=(const std::string& lhs,
2430 const basic_fbstring<E, T, A, S>& rhs) {
2431 return !(lhs == rhs);
2434 #if !defined(_LIBSTDCXX_FBSTRING)
2435 typedef basic_fbstring<char> fbstring;
2438 // fbstring is relocatable
2439 template <class T, class R, class A, class S>
2440 FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);
2443 _GLIBCXX_END_NAMESPACE_VERSION
2446 } // namespace folly
2448 #ifndef _LIBSTDCXX_FBSTRING
2450 // Hash functions to make fbstring usable with e.g. hash_map
2452 // Handle interaction with different C++ standard libraries, which
2453 // expect these types to be in different namespaces.
2457 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2458 size_t operator()(const folly::basic_fbstring<C> & s) const {
2459 return hash<const C*>::operator()(s.c_str());
2464 struct hash< ::folly::fbstring> {
2465 size_t operator()(const ::folly::fbstring& s) const {
2466 return ::folly::hash::fnv32_buf(s.data(), s.size());
2472 #if defined(_GLIBCXX_SYMVER) && !defined(__BIONIC__)
2473 namespace __gnu_cxx {
2476 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2477 size_t operator()(const folly::basic_fbstring<C> & s) const {
2478 return hash<const C*>::operator()(s.c_str());
2483 struct hash< ::folly::fbstring> {
2484 size_t operator()(const ::folly::fbstring& s) const {
2485 return ::folly::hash::fnv32_buf(s.data(), s.size());
2490 #endif // _GLIBCXX_SYMVER && !__BIONIC__
2491 #endif // _LIBSTDCXX_FBSTRING
2493 #pragma GCC diagnostic pop
2495 #undef FBSTRING_DISABLE_ADDRESS_SANITIZER
2497 #undef FBSTRING_LIKELY
2498 #undef FBSTRING_UNLIKELY
2500 #endif // FOLLY_BASE_FBSTRING_H_