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 &&
871 static_cast<size_t>(small_[maxSmallSize])
872 <= static_cast<size_t>(maxSmallSize));
873 return static_cast<size_t>(maxSmallSize)
874 - static_cast<size_t>(small_[maxSmallSize]);
877 void setSmallSize(size_t s) {
878 // Warning: this should work with uninitialized strings too,
879 // so don't assume anything about the previous value of
880 // small_[maxSmallSize].
881 assert(s <= maxSmallSize);
882 small_[maxSmallSize] = maxSmallSize - s;
886 #if defined(__GNUC__) && !defined(__clang__)
887 # pragma GCC diagnostic pop
890 #ifndef _LIBSTDCXX_FBSTRING
892 * Dummy fbstring core that uses an actual std::string. This doesn't
893 * make any sense - it's just for testing purposes.
895 template <class Char>
896 class dummy_fbstring_core {
898 dummy_fbstring_core() {
900 dummy_fbstring_core(const dummy_fbstring_core& another)
901 : backend_(another.backend_) {
903 dummy_fbstring_core(const Char * s, size_t n)
906 void swap(dummy_fbstring_core & rhs) {
907 backend_.swap(rhs.backend_);
909 const Char * data() const {
910 return backend_.data();
912 Char * mutable_data() {
913 //assert(!backend_.empty());
914 return &*backend_.begin();
916 void shrink(size_t delta) {
917 assert(delta <= size());
918 backend_.resize(size() - delta);
920 Char * expand_noinit(size_t delta) {
921 auto const sz = size();
922 backend_.resize(size() + delta);
923 return backend_.data() + sz;
925 void push_back(Char c) {
926 backend_.push_back(c);
928 size_t size() const {
929 return backend_.size();
931 size_t capacity() const {
932 return backend_.capacity();
934 bool isShared() const {
937 void reserve(size_t minCapacity) {
938 backend_.reserve(minCapacity);
942 std::basic_string<Char> backend_;
944 #endif // !_LIBSTDCXX_FBSTRING
947 * This is the basic_string replacement. For conformity,
948 * basic_fbstring takes the same template parameters, plus the last
949 * one which is the core.
951 #ifdef _LIBSTDCXX_FBSTRING
952 template <typename E, class T, class A, class Storage>
954 template <typename E,
955 class T = std::char_traits<E>,
956 class A = std::allocator<E>,
957 class Storage = fbstring_core<E> >
959 class basic_fbstring {
963 void (*throw_exc)(const char*),
965 if (!condition) throw_exc(msg);
968 bool isSane() const {
971 empty() == (size() == 0) &&
972 empty() == (begin() == end()) &&
973 size() <= max_size() &&
974 capacity() <= max_size() &&
975 size() <= capacity() &&
976 (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0');
980 friend struct Invariant;
983 explicit Invariant(const basic_fbstring& s) : s_(s) {
990 const basic_fbstring& s_;
992 explicit Invariant(const basic_fbstring&) {}
994 Invariant& operator=(const Invariant&);
999 typedef T traits_type;
1000 typedef typename traits_type::char_type value_type;
1001 typedef A allocator_type;
1002 typedef typename A::size_type size_type;
1003 typedef typename A::difference_type difference_type;
1005 typedef typename A::reference reference;
1006 typedef typename A::const_reference const_reference;
1007 typedef typename A::pointer pointer;
1008 typedef typename A::const_pointer const_pointer;
1010 typedef E* iterator;
1011 typedef const E* const_iterator;
1012 typedef std::reverse_iterator<iterator
1013 #ifdef NO_ITERATOR_TRAITS
1017 typedef std::reverse_iterator<const_iterator
1018 #ifdef NO_ITERATOR_TRAITS
1021 > const_reverse_iterator;
1023 static const size_type npos; // = size_type(-1)
1026 static void procrustes(size_type& n, size_type nmax) {
1027 if (n > nmax) n = nmax;
1031 // C++11 21.4.2 construct/copy/destroy
1032 explicit basic_fbstring(const A& a = A()) noexcept {
1035 basic_fbstring(const basic_fbstring& str)
1036 : store_(str.store_) {
1040 basic_fbstring(basic_fbstring&& goner) noexcept
1041 : store_(std::move(goner.store_)) {
1044 #ifndef _LIBSTDCXX_FBSTRING
1045 // This is defined for compatibility with std::string
1046 /* implicit */ basic_fbstring(const std::string& str)
1047 : store_(str.data(), str.size()) {
1051 basic_fbstring(const basic_fbstring& str, size_type pos,
1052 size_type n = npos, const A& a = A()) {
1053 assign(str, pos, n);
1056 /* implicit */ basic_fbstring(const value_type* s, const A& a = A())
1058 ? traits_type::length(s)
1060 std::__throw_logic_error(
1061 "basic_fbstring: null pointer initializer not valid");
1066 basic_fbstring(const value_type* s, size_type n, const A& a = A())
1070 basic_fbstring(size_type n, value_type c, const A& a = A()) {
1071 auto const data = store_.expand_noinit(n);
1072 fbstring_detail::pod_fill(data, data + n, c);
1073 store_.writeTerminator();
1076 template <class InIt>
1077 basic_fbstring(InIt begin, InIt end,
1078 typename std::enable_if<
1079 !std::is_same<typename std::remove_const<InIt>::type,
1080 value_type*>::value, const A>::type & a = A()) {
1084 // Specialization for const char*, const char*
1085 basic_fbstring(const value_type* b, const value_type* e)
1086 : store_(b, e - b) {
1089 // Nonstandard constructor
1090 basic_fbstring(value_type *s, size_type n, size_type c,
1091 AcquireMallocatedString a)
1092 : store_(s, n, c, a) {
1095 // Construction from initialization list
1096 basic_fbstring(std::initializer_list<value_type> il) {
1097 assign(il.begin(), il.end());
1100 ~basic_fbstring() noexcept {
1103 basic_fbstring& operator=(const basic_fbstring& lhs) {
1104 if (FBSTRING_UNLIKELY(&lhs == this)) {
1107 auto const oldSize = size();
1108 auto const srcSize = lhs.size();
1109 if (capacity() >= srcSize && !store_.isShared()) {
1110 // great, just copy the contents
1111 if (oldSize < srcSize)
1112 store_.expand_noinit(srcSize - oldSize);
1114 store_.shrink(oldSize - srcSize);
1115 assert(size() == srcSize);
1116 fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin());
1117 store_.writeTerminator();
1119 // need to reallocate, so we may as well create a brand new string
1120 basic_fbstring(lhs).swap(*this);
1126 basic_fbstring& operator=(basic_fbstring&& goner) noexcept {
1127 if (FBSTRING_UNLIKELY(&goner == this)) {
1128 // Compatibility with std::basic_string<>,
1129 // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.
1132 // No need of this anymore
1133 this->~basic_fbstring();
1134 // Move the goner into this
1135 new(&store_) fbstring_core<E>(std::move(goner.store_));
1139 #ifndef _LIBSTDCXX_FBSTRING
1140 // Compatibility with std::string
1141 basic_fbstring & operator=(const std::string & rhs) {
1142 return assign(rhs.data(), rhs.size());
1145 // Compatibility with std::string
1146 std::string toStdString() const {
1147 return std::string(data(), size());
1150 // A lot of code in fbcode still uses this method, so keep it here for now.
1151 const basic_fbstring& toStdString() const {
1156 basic_fbstring& operator=(const value_type* s) {
1160 basic_fbstring& operator=(value_type c) {
1162 store_.expand_noinit(1);
1163 } else if (store_.isShared()) {
1164 basic_fbstring(1, c).swap(*this);
1167 store_.shrink(size() - 1);
1169 *store_.mutable_data() = c;
1170 store_.writeTerminator();
1174 basic_fbstring& operator=(std::initializer_list<value_type> il) {
1175 return assign(il.begin(), il.end());
1178 // C++11 21.4.3 iterators:
1179 iterator begin() { return store_.mutable_data(); }
1181 const_iterator begin() const { return store_.data(); }
1183 const_iterator cbegin() const { return begin(); }
1186 return store_.mutable_data() + store_.size();
1189 const_iterator end() const {
1190 return store_.data() + store_.size();
1193 const_iterator cend() const { return end(); }
1195 reverse_iterator rbegin() {
1196 return reverse_iterator(end());
1199 const_reverse_iterator rbegin() const {
1200 return const_reverse_iterator(end());
1203 const_reverse_iterator crbegin() const { return rbegin(); }
1205 reverse_iterator rend() {
1206 return reverse_iterator(begin());
1209 const_reverse_iterator rend() const {
1210 return const_reverse_iterator(begin());
1213 const_reverse_iterator crend() const { return rend(); }
1216 // C++11 21.4.5, element access:
1217 const value_type& front() const { return *begin(); }
1218 const value_type& back() const {
1220 // Should be begin()[size() - 1], but that branches twice
1221 return *(end() - 1);
1223 value_type& front() { return *begin(); }
1224 value_type& back() {
1226 // Should be begin()[size() - 1], but that branches twice
1227 return *(end() - 1);
1234 // C++11 21.4.4 capacity:
1235 size_type size() const { return store_.size(); }
1237 size_type length() const { return size(); }
1239 size_type max_size() const {
1240 return std::numeric_limits<size_type>::max();
1243 void resize(const size_type n, const value_type c = value_type()) {
1244 auto size = this->size();
1246 store_.shrink(size - n);
1248 // Do this in two steps to minimize slack memory copied (see
1250 auto const capacity = this->capacity();
1251 assert(capacity >= size);
1252 if (size < capacity) {
1253 auto delta = std::min(n, capacity) - size;
1254 store_.expand_noinit(delta);
1255 fbstring_detail::pod_fill(begin() + size, end(), c);
1258 store_.writeTerminator();
1263 auto const delta = n - size;
1264 store_.expand_noinit(delta);
1265 fbstring_detail::pod_fill(end() - delta, end(), c);
1266 store_.writeTerminator();
1268 assert(this->size() == n);
1271 size_type capacity() const { return store_.capacity(); }
1273 void reserve(size_type res_arg = 0) {
1274 enforce(res_arg <= max_size(), std::__throw_length_error, "");
1275 store_.reserve(res_arg);
1278 void shrink_to_fit() {
1279 // Shrink only if slack memory is sufficiently large
1280 if (capacity() < size() * 3 / 2) {
1283 basic_fbstring(cbegin(), cend()).swap(*this);
1286 void clear() { resize(0); }
1288 bool empty() const { return size() == 0; }
1290 // C++11 21.4.5 element access:
1291 const_reference operator[](size_type pos) const {
1292 return *(c_str() + pos);
1295 reference operator[](size_type pos) {
1296 if (pos == size()) {
1297 // Just call c_str() to make sure '\0' is present
1300 return *(begin() + pos);
1303 const_reference at(size_type n) const {
1304 enforce(n <= size(), std::__throw_out_of_range, "");
1308 reference at(size_type n) {
1309 enforce(n < size(), std::__throw_out_of_range, "");
1313 // C++11 21.4.6 modifiers:
1314 basic_fbstring& operator+=(const basic_fbstring& str) {
1318 basic_fbstring& operator+=(const value_type* s) {
1322 basic_fbstring& operator+=(const value_type c) {
1327 basic_fbstring& operator+=(std::initializer_list<value_type> il) {
1332 basic_fbstring& append(const basic_fbstring& str) {
1334 auto desiredSize = size() + str.size();
1336 append(str.data(), str.size());
1337 assert(size() == desiredSize);
1341 basic_fbstring& append(const basic_fbstring& str, const size_type pos,
1343 const size_type sz = str.size();
1344 enforce(pos <= sz, std::__throw_out_of_range, "");
1345 procrustes(n, sz - pos);
1346 return append(str.data() + pos, n);
1349 basic_fbstring& append(const value_type* s, size_type n) {
1351 Invariant checker(*this);
1354 if (FBSTRING_UNLIKELY(!n)) {
1355 // Unlikely but must be done
1358 auto const oldSize = size();
1359 auto const oldData = data();
1360 // Check for aliasing (rare). We could use "<=" here but in theory
1361 // those do not work for pointers unless the pointers point to
1362 // elements in the same array. For that reason we use
1363 // std::less_equal, which is guaranteed to offer a total order
1364 // over pointers. See discussion at http://goo.gl/Cy2ya for more
1366 std::less_equal<const value_type*> le;
1367 if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
1368 assert(le(s + n, oldData + oldSize));
1369 const size_type offset = s - oldData;
1370 store_.reserve(oldSize + n);
1371 // Restore the source
1372 s = data() + offset;
1374 // Warning! Repeated appends with short strings may actually incur
1375 // practically quadratic performance. Avoid that by pushing back
1376 // the first character (which ensures exponential growth) and then
1377 // appending the rest normally. Worst case the append may incur a
1378 // second allocation but that will be rare.
1381 memcpy(store_.expand_noinit(n), s, n * sizeof(value_type));
1382 assert(size() == oldSize + n + 1);
1386 basic_fbstring& append(const value_type* s) {
1387 return append(s, traits_type::length(s));
1390 basic_fbstring& append(size_type n, value_type c) {
1391 resize(size() + n, c);
1395 template<class InputIterator>
1396 basic_fbstring& append(InputIterator first, InputIterator last) {
1397 insert(end(), first, last);
1401 basic_fbstring& append(std::initializer_list<value_type> il) {
1402 return append(il.begin(), il.end());
1405 void push_back(const value_type c) { // primitive
1406 store_.push_back(c);
1409 basic_fbstring& assign(const basic_fbstring& str) {
1410 if (&str == this) return *this;
1411 return assign(str.data(), str.size());
1414 basic_fbstring& assign(basic_fbstring&& str) {
1415 return *this = std::move(str);
1418 basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
1420 const size_type sz = str.size();
1421 enforce(pos <= sz, std::__throw_out_of_range, "");
1422 procrustes(n, sz - pos);
1423 return assign(str.data() + pos, n);
1426 basic_fbstring& assign(const value_type* s, const size_type n) {
1427 Invariant checker(*this);
1430 std::copy(s, s + n, begin());
1432 assert(size() == n);
1434 const value_type *const s2 = s + size();
1435 std::copy(s, s2, begin());
1436 append(s2, n - size());
1437 assert(size() == n);
1439 store_.writeTerminator();
1440 assert(size() == n);
1444 basic_fbstring& assign(const value_type* s) {
1445 return assign(s, traits_type::length(s));
1448 basic_fbstring& assign(std::initializer_list<value_type> il) {
1449 return assign(il.begin(), il.end());
1452 template <class ItOrLength, class ItOrChar>
1453 basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {
1454 return replace(begin(), end(), first_or_n, last_or_c);
1457 basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {
1458 return insert(pos1, str.data(), str.size());
1461 basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
1462 size_type pos2, size_type n) {
1463 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1464 procrustes(n, str.length() - pos2);
1465 return insert(pos1, str.data() + pos2, n);
1468 basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {
1469 enforce(pos <= length(), std::__throw_out_of_range, "");
1470 insert(begin() + pos, s, s + n);
1474 basic_fbstring& insert(size_type pos, const value_type* s) {
1475 return insert(pos, s, traits_type::length(s));
1478 basic_fbstring& insert(size_type pos, size_type n, value_type c) {
1479 enforce(pos <= length(), std::__throw_out_of_range, "");
1480 insert(begin() + pos, n, c);
1484 iterator insert(const_iterator p, const value_type c) {
1485 const size_type pos = p - begin();
1487 return begin() + pos;
1491 template <int i> class Selector {};
1493 iterator insertImplDiscr(const_iterator p,
1494 size_type n, value_type c, Selector<1>) {
1495 Invariant checker(*this);
1497 auto const pos = p - begin();
1498 assert(p >= begin() && p <= end());
1499 if (capacity() - size() < n) {
1500 const size_type sz = p - begin();
1501 reserve(size() + n);
1504 const iterator oldEnd = end();
1505 if (n < size_type(oldEnd - p)) {
1506 append(oldEnd - n, oldEnd);
1508 // reverse_iterator(oldEnd - n),
1509 // reverse_iterator(p),
1510 // reverse_iterator(oldEnd));
1511 fbstring_detail::pod_move(&*p, &*oldEnd - n,
1513 std::fill(begin() + pos, begin() + pos + n, c);
1515 append(n - (end() - p), c);
1516 append(iterator(p), oldEnd);
1517 std::fill(iterator(p), oldEnd, c);
1519 store_.writeTerminator();
1520 return begin() + pos;
1523 template<class InputIter>
1524 iterator insertImplDiscr(const_iterator i,
1525 InputIter b, InputIter e, Selector<0>) {
1526 return insertImpl(i, b, e,
1527 typename std::iterator_traits<InputIter>::iterator_category());
1530 template <class FwdIterator>
1531 iterator insertImpl(const_iterator i,
1532 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1533 Invariant checker(*this);
1535 const size_type pos = i - begin();
1536 const typename std::iterator_traits<FwdIterator>::difference_type n2 =
1537 std::distance(s1, s2);
1539 using namespace fbstring_detail;
1540 assert(pos <= size());
1542 const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
1543 capacity() - size();
1545 // realloc the string
1546 reserve(size() + n2);
1549 if (pos + n2 <= size()) {
1550 const iterator tailBegin = end() - n2;
1551 store_.expand_noinit(n2);
1552 fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2);
1553 std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i),
1554 reverse_iterator(tailBegin + n2));
1555 std::copy(s1, s2, begin() + pos);
1558 const size_type old_size = size();
1559 std::advance(t, old_size - pos);
1560 const size_t newElems = std::distance(t, s2);
1561 store_.expand_noinit(n2);
1562 std::copy(t, s2, begin() + old_size);
1563 fbstring_detail::pod_copy(data() + pos, data() + old_size,
1564 begin() + old_size + newElems);
1565 std::copy(s1, t, begin() + pos);
1567 store_.writeTerminator();
1568 return begin() + pos;
1571 template <class InputIterator>
1572 iterator insertImpl(const_iterator i,
1573 InputIterator b, InputIterator e,
1574 std::input_iterator_tag) {
1575 const auto pos = i - begin();
1576 basic_fbstring temp(begin(), i);
1577 for (; b != e; ++b) {
1580 temp.append(i, cend());
1582 return begin() + pos;
1586 template <class ItOrLength, class ItOrChar>
1587 iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
1588 Selector<std::numeric_limits<ItOrLength>::is_specialized> sel;
1589 return insertImplDiscr(p, first_or_n, last_or_c, sel);
1592 iterator insert(const_iterator p, std::initializer_list<value_type> il) {
1593 return insert(p, il.begin(), il.end());
1596 basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
1597 Invariant checker(*this);
1599 enforce(pos <= length(), std::__throw_out_of_range, "");
1600 procrustes(n, length() - pos);
1601 std::copy(begin() + pos + n, end(), begin() + pos);
1602 resize(length() - n);
1606 iterator erase(iterator position) {
1607 const size_type pos(position - begin());
1608 enforce(pos <= size(), std::__throw_out_of_range, "");
1610 return begin() + pos;
1613 iterator erase(iterator first, iterator last) {
1614 const size_type pos(first - begin());
1615 erase(pos, last - first);
1616 return begin() + pos;
1619 // Replaces at most n1 chars of *this, starting with pos1 with the
1621 basic_fbstring& replace(size_type pos1, size_type n1,
1622 const basic_fbstring& str) {
1623 return replace(pos1, n1, str.data(), str.size());
1626 // Replaces at most n1 chars of *this, starting with pos1,
1627 // with at most n2 chars of str starting with pos2
1628 basic_fbstring& replace(size_type pos1, size_type n1,
1629 const basic_fbstring& str,
1630 size_type pos2, size_type n2) {
1631 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1632 return replace(pos1, n1, str.data() + pos2,
1633 std::min(n2, str.size() - pos2));
1636 // Replaces at most n1 chars of *this, starting with pos, with chars from s
1637 basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
1638 return replace(pos, n1, s, traits_type::length(s));
1641 // Replaces at most n1 chars of *this, starting with pos, with n2
1644 // consolidated with
1646 // Replaces at most n1 chars of *this, starting with pos, with at
1647 // most n2 chars of str. str must have at least n2 chars.
1648 template <class StrOrLength, class NumOrChar>
1649 basic_fbstring& replace(size_type pos, size_type n1,
1650 StrOrLength s_or_n2, NumOrChar n_or_c) {
1651 Invariant checker(*this);
1653 enforce(pos <= size(), std::__throw_out_of_range, "");
1654 procrustes(n1, length() - pos);
1655 const iterator b = begin() + pos;
1656 return replace(b, b + n1, s_or_n2, n_or_c);
1659 basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {
1660 return replace(i1, i2, str.data(), str.length());
1663 basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
1664 return replace(i1, i2, s, traits_type::length(s));
1668 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1669 const value_type* s, size_type n,
1672 assert(begin() <= i1 && i1 <= end());
1673 assert(begin() <= i2 && i2 <= end());
1674 return replace(i1, i2, s, s + n);
1677 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1678 size_type n2, value_type c, Selector<1>) {
1679 const size_type n1 = i2 - i1;
1681 std::fill(i1, i1 + n2, c);
1684 std::fill(i1, i2, c);
1685 insert(i2, n2 - n1, c);
1691 template <class InputIter>
1692 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1693 InputIter b, InputIter e,
1695 replaceImpl(i1, i2, b, e,
1696 typename std::iterator_traits<InputIter>::iterator_category());
1701 template <class FwdIterator>
1702 bool replaceAliased(iterator i1, iterator i2,
1703 FwdIterator s1, FwdIterator s2, std::false_type) {
1707 template <class FwdIterator>
1708 bool replaceAliased(iterator i1, iterator i2,
1709 FwdIterator s1, FwdIterator s2, std::true_type) {
1710 static const std::less_equal<const value_type*> le =
1711 std::less_equal<const value_type*>();
1712 const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());
1716 // Aliased replace, copy to new string
1717 basic_fbstring temp;
1718 temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));
1719 temp.append(begin(), i1).append(s1, s2).append(i2, end());
1724 template <class FwdIterator>
1725 void replaceImpl(iterator i1, iterator i2,
1726 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1727 Invariant checker(*this);
1730 // Handle aliased replace
1731 if (replaceAliased(i1, i2, s1, s2,
1732 std::integral_constant<bool,
1733 std::is_same<FwdIterator, iterator>::value ||
1734 std::is_same<FwdIterator, const_iterator>::value>())) {
1738 auto const n1 = i2 - i1;
1740 auto const n2 = std::distance(s1, s2);
1745 std::copy(s1, s2, i1);
1749 fbstring_detail::copy_n(s1, n1, i1);
1750 std::advance(s1, n1);
1756 template <class InputIterator>
1757 void replaceImpl(iterator i1, iterator i2,
1758 InputIterator b, InputIterator e, std::input_iterator_tag) {
1759 basic_fbstring temp(begin(), i1);
1760 temp.append(b, e).append(i2, end());
1765 template <class T1, class T2>
1766 basic_fbstring& replace(iterator i1, iterator i2,
1767 T1 first_or_n_or_s, T2 last_or_c_or_n) {
1769 num1 = std::numeric_limits<T1>::is_specialized,
1770 num2 = std::numeric_limits<T2>::is_specialized;
1771 return replaceImplDiscr(
1772 i1, i2, first_or_n_or_s, last_or_c_or_n,
1773 Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>());
1776 size_type copy(value_type* s, size_type n, size_type pos = 0) const {
1777 enforce(pos <= size(), std::__throw_out_of_range, "");
1778 procrustes(n, size() - pos);
1780 fbstring_detail::pod_copy(
1787 void swap(basic_fbstring& rhs) {
1788 store_.swap(rhs.store_);
1791 const value_type* c_str() const {
1792 return store_.c_str();
1795 const value_type* data() const { return c_str(); }
1797 allocator_type get_allocator() const {
1798 return allocator_type();
1801 size_type find(const basic_fbstring& str, size_type pos = 0) const {
1802 return find(str.data(), pos, str.length());
1805 size_type find(const value_type* needle, const size_type pos,
1806 const size_type nsize) const {
1807 if (!nsize) return pos;
1808 auto const size = this->size();
1809 // nsize + pos can overflow (eg pos == npos), guard against that by checking
1810 // that nsize + pos does not wrap around.
1811 if (nsize + pos > size || nsize + pos < pos) return npos;
1812 // Don't use std::search, use a Boyer-Moore-like trick by comparing
1813 // the last characters first
1814 auto const haystack = data();
1815 auto const nsize_1 = nsize - 1;
1816 auto const lastNeedle = needle[nsize_1];
1818 // Boyer-Moore skip value for the last char in the needle. Zero is
1819 // not a valid value; skip will be computed the first time it's
1823 const E * i = haystack + pos;
1824 auto iEnd = haystack + size - nsize_1;
1827 // Boyer-Moore: match the last element in the needle
1828 while (i[nsize_1] != lastNeedle) {
1834 // Here we know that the last char matches
1835 // Continue in pedestrian mode
1836 for (size_t j = 0; ; ) {
1838 if (i[j] != needle[j]) {
1839 // Not found, we can skip
1840 // Compute the skip value lazily
1843 while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {
1850 // Check if done searching
1853 return i - haystack;
1860 size_type find(const value_type* s, size_type pos = 0) const {
1861 return find(s, pos, traits_type::length(s));
1864 size_type find (value_type c, size_type pos = 0) const {
1865 return find(&c, pos, 1);
1868 size_type rfind(const basic_fbstring& str, size_type pos = npos) const {
1869 return rfind(str.data(), pos, str.length());
1872 size_type rfind(const value_type* s, size_type pos, size_type n) const {
1873 if (n > length()) return npos;
1874 pos = std::min(pos, length() - n);
1875 if (n == 0) return pos;
1877 const_iterator i(begin() + pos);
1879 if (traits_type::eq(*i, *s)
1880 && traits_type::compare(&*i, s, n) == 0) {
1883 if (i == begin()) break;
1888 size_type rfind(const value_type* s, size_type pos = npos) const {
1889 return rfind(s, pos, traits_type::length(s));
1892 size_type rfind(value_type c, size_type pos = npos) const {
1893 return rfind(&c, pos, 1);
1896 size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {
1897 return find_first_of(str.data(), pos, str.length());
1900 size_type find_first_of(const value_type* s,
1901 size_type pos, size_type n) const {
1902 if (pos > length() || n == 0) return npos;
1903 const_iterator i(begin() + pos),
1905 for (; i != finish; ++i) {
1906 if (traits_type::find(s, n, *i) != 0) {
1913 size_type find_first_of(const value_type* s, size_type pos = 0) const {
1914 return find_first_of(s, pos, traits_type::length(s));
1917 size_type find_first_of(value_type c, size_type pos = 0) const {
1918 return find_first_of(&c, pos, 1);
1921 size_type find_last_of (const basic_fbstring& str,
1922 size_type pos = npos) const {
1923 return find_last_of(str.data(), pos, str.length());
1926 size_type find_last_of (const value_type* s, size_type pos,
1927 size_type n) const {
1928 if (!empty() && n > 0) {
1929 pos = std::min(pos, length() - 1);
1930 const_iterator i(begin() + pos);
1932 if (traits_type::find(s, n, *i) != 0) {
1935 if (i == begin()) break;
1941 size_type find_last_of (const value_type* s,
1942 size_type pos = npos) const {
1943 return find_last_of(s, pos, traits_type::length(s));
1946 size_type find_last_of (value_type c, size_type pos = npos) const {
1947 return find_last_of(&c, pos, 1);
1950 size_type find_first_not_of(const basic_fbstring& str,
1951 size_type pos = 0) const {
1952 return find_first_not_of(str.data(), pos, str.size());
1955 size_type find_first_not_of(const value_type* s, size_type pos,
1956 size_type n) const {
1957 if (pos < length()) {
1961 for (; i != finish; ++i) {
1962 if (traits_type::find(s, n, *i) == 0) {
1970 size_type find_first_not_of(const value_type* s,
1971 size_type pos = 0) const {
1972 return find_first_not_of(s, pos, traits_type::length(s));
1975 size_type find_first_not_of(value_type c, size_type pos = 0) const {
1976 return find_first_not_of(&c, pos, 1);
1979 size_type find_last_not_of(const basic_fbstring& str,
1980 size_type pos = npos) const {
1981 return find_last_not_of(str.data(), pos, str.length());
1984 size_type find_last_not_of(const value_type* s, size_type pos,
1985 size_type n) const {
1986 if (!this->empty()) {
1987 pos = std::min(pos, size() - 1);
1988 const_iterator i(begin() + pos);
1990 if (traits_type::find(s, n, *i) == 0) {
1993 if (i == begin()) break;
1999 size_type find_last_not_of(const value_type* s,
2000 size_type pos = npos) const {
2001 return find_last_not_of(s, pos, traits_type::length(s));
2004 size_type find_last_not_of (value_type c, size_type pos = npos) const {
2005 return find_last_not_of(&c, pos, 1);
2008 basic_fbstring substr(size_type pos = 0, size_type n = npos) const {
2009 enforce(pos <= size(), std::__throw_out_of_range, "");
2010 return basic_fbstring(data() + pos, std::min(n, size() - pos));
2013 int compare(const basic_fbstring& str) const {
2014 // FIX due to Goncalo N M de Carvalho July 18, 2005
2015 return compare(0, size(), str);
2018 int compare(size_type pos1, size_type n1,
2019 const basic_fbstring& str) const {
2020 return compare(pos1, n1, str.data(), str.size());
2023 int compare(size_type pos1, size_type n1,
2024 const value_type* s) const {
2025 return compare(pos1, n1, s, traits_type::length(s));
2028 int compare(size_type pos1, size_type n1,
2029 const value_type* s, size_type n2) const {
2030 enforce(pos1 <= size(), std::__throw_out_of_range, "");
2031 procrustes(n1, size() - pos1);
2032 // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!
2033 const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));
2034 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2037 int compare(size_type pos1, size_type n1,
2038 const basic_fbstring& str,
2039 size_type pos2, size_type n2) const {
2040 enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
2041 return compare(pos1, n1, str.data() + pos2,
2042 std::min(n2, str.size() - pos2));
2045 // Code from Jean-Francois Bastien (03/26/2007)
2046 int compare(const value_type* s) const {
2047 // Could forward to compare(0, size(), s, traits_type::length(s))
2048 // but that does two extra checks
2049 const size_type n1(size()), n2(traits_type::length(s));
2050 const int r = traits_type::compare(data(), s, std::min(n1, n2));
2051 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2059 // non-member functions
2061 template <typename E, class T, class A, class S>
2063 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2064 const basic_fbstring<E, T, A, S>& rhs) {
2066 basic_fbstring<E, T, A, S> result;
2067 result.reserve(lhs.size() + rhs.size());
2068 result.append(lhs).append(rhs);
2069 return std::move(result);
2073 template <typename E, class T, class A, class S>
2075 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2076 const basic_fbstring<E, T, A, S>& rhs) {
2077 return std::move(lhs.append(rhs));
2081 template <typename E, class T, class A, class S>
2083 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2084 basic_fbstring<E, T, A, S>&& rhs) {
2085 if (rhs.capacity() >= lhs.size() + rhs.size()) {
2086 // Good, at least we don't need to reallocate
2087 return std::move(rhs.insert(0, lhs));
2089 // Meh, no go. Forward to operator+(const&, const&).
2090 auto const& rhsC = rhs;
2095 template <typename E, class T, class A, class S>
2097 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2098 basic_fbstring<E, T, A, S>&& rhs) {
2099 return std::move(lhs.append(rhs));
2102 template <typename E, class T, class A, class S>
2104 basic_fbstring<E, T, A, S> operator+(
2105 const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2106 const basic_fbstring<E, T, A, S>& rhs) {
2108 basic_fbstring<E, T, A, S> result;
2109 const typename basic_fbstring<E, T, A, S>::size_type len =
2110 basic_fbstring<E, T, A, S>::traits_type::length(lhs);
2111 result.reserve(len + rhs.size());
2112 result.append(lhs, len).append(rhs);
2116 template <typename E, class T, class A, class S>
2118 basic_fbstring<E, T, A, S> operator+(
2119 typename basic_fbstring<E, T, A, S>::value_type lhs,
2120 const basic_fbstring<E, T, A, S>& rhs) {
2122 basic_fbstring<E, T, A, S> result;
2123 result.reserve(1 + rhs.size());
2124 result.push_back(lhs);
2129 template <typename E, class T, class A, class S>
2131 basic_fbstring<E, T, A, S> operator+(
2132 const basic_fbstring<E, T, A, S>& lhs,
2133 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2135 typedef typename basic_fbstring<E, T, A, S>::size_type size_type;
2136 typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;
2138 basic_fbstring<E, T, A, S> result;
2139 const size_type len = traits_type::length(rhs);
2140 result.reserve(lhs.size() + len);
2141 result.append(lhs).append(rhs, len);
2145 template <typename E, class T, class A, class S>
2147 basic_fbstring<E, T, A, S> operator+(
2148 const basic_fbstring<E, T, A, S>& lhs,
2149 typename basic_fbstring<E, T, A, S>::value_type rhs) {
2151 basic_fbstring<E, T, A, S> result;
2152 result.reserve(lhs.size() + 1);
2154 result.push_back(rhs);
2158 template <typename E, class T, class A, class S>
2160 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2161 const basic_fbstring<E, T, A, S>& rhs) {
2162 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; }
2164 template <typename E, class T, class A, class S>
2166 bool operator==(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2167 const basic_fbstring<E, T, A, S>& rhs) {
2168 return rhs == lhs; }
2170 template <typename E, class T, class A, class S>
2172 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2173 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2174 return lhs.compare(rhs) == 0; }
2176 template <typename E, class T, class A, class S>
2178 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2179 const basic_fbstring<E, T, A, S>& rhs) {
2180 return !(lhs == rhs); }
2182 template <typename E, class T, class A, class S>
2184 bool operator!=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2185 const basic_fbstring<E, T, A, S>& rhs) {
2186 return !(lhs == rhs); }
2188 template <typename E, class T, class A, class S>
2190 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2191 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2192 return !(lhs == rhs); }
2194 template <typename E, class T, class A, class S>
2196 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2197 const basic_fbstring<E, T, A, S>& rhs) {
2198 return lhs.compare(rhs) < 0; }
2200 template <typename E, class T, class A, class S>
2202 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2203 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2204 return lhs.compare(rhs) < 0; }
2206 template <typename E, class T, class A, class S>
2208 bool operator<(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2209 const basic_fbstring<E, T, A, S>& rhs) {
2210 return rhs.compare(lhs) > 0; }
2212 template <typename E, class T, class A, class S>
2214 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2215 const basic_fbstring<E, T, A, S>& rhs) {
2218 template <typename E, class T, class A, class S>
2220 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2221 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2224 template <typename E, class T, class A, class S>
2226 bool operator>(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2227 const basic_fbstring<E, T, A, S>& rhs) {
2230 template <typename E, class T, class A, class S>
2232 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2233 const basic_fbstring<E, T, A, S>& rhs) {
2234 return !(rhs < lhs); }
2236 template <typename E, class T, class A, class S>
2238 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2239 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2240 return !(rhs < lhs); }
2242 template <typename E, class T, class A, class S>
2244 bool operator<=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2245 const basic_fbstring<E, T, A, S>& rhs) {
2246 return !(rhs < lhs); }
2248 template <typename E, class T, class A, class S>
2250 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2251 const basic_fbstring<E, T, A, S>& rhs) {
2252 return !(lhs < rhs); }
2254 template <typename E, class T, class A, class S>
2256 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2257 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2258 return !(lhs < rhs); }
2260 template <typename E, class T, class A, class S>
2262 bool operator>=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2263 const basic_fbstring<E, T, A, S>& rhs) {
2264 return !(lhs < rhs);
2268 template <typename E, class T, class A, class S>
2269 void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {
2273 // TODO: make this faster.
2274 template <typename E, class T, class A, class S>
2277 typename basic_fbstring<E, T, A, S>::value_type,
2278 typename basic_fbstring<E, T, A, S>::traits_type>&
2280 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2281 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2282 basic_fbstring<E, T, A, S>& str) {
2283 typename std::basic_istream<E, T>::sentry sentry(is);
2284 typedef std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2285 typename basic_fbstring<E, T, A, S>::traits_type>
2287 typedef typename __istream_type::ios_base __ios_base;
2288 size_t extracted = 0;
2289 auto err = __ios_base::goodbit;
2291 auto n = is.width();
2296 auto got = is.rdbuf()->sgetc();
2297 for (; extracted != n && got != T::eof() && !isspace(got); ++extracted) {
2298 // Whew. We get to store this guy
2300 got = is.rdbuf()->snextc();
2302 if (got == T::eof()) {
2303 err |= __ios_base::eofbit;
2308 err |= __ios_base::failbit;
2316 template <typename E, class T, class A, class S>
2318 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2319 typename basic_fbstring<E, T, A, S>::traits_type>&
2321 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2322 typename basic_fbstring<E, T, A, S>::traits_type>& os,
2323 const basic_fbstring<E, T, A, S>& str) {
2325 typename std::basic_ostream<
2326 typename basic_fbstring<E, T, A, S>::value_type,
2327 typename basic_fbstring<E, T, A, S>::traits_type>::sentry __s(os);
2329 typedef std::ostreambuf_iterator<
2330 typename basic_fbstring<E, T, A, S>::value_type,
2331 typename basic_fbstring<E, T, A, S>::traits_type> _Ip;
2332 size_t __len = str.size();
2334 (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
2335 if (__pad_and_output(_Ip(os),
2337 __left ? str.data() + __len : str.data(),
2340 os.fill()).failed()) {
2341 os.setstate(std::ios_base::badbit | std::ios_base::failbit);
2345 std::__ostream_insert(os, str.data(), str.size());
2350 #ifndef _LIBSTDCXX_FBSTRING
2352 template <typename E, class T, class A, class S>
2354 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2355 typename basic_fbstring<E, T, A, S>::traits_type>&
2357 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2358 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2359 basic_fbstring<E, T, A, S>& str,
2360 typename basic_fbstring<E, T, A, S>::value_type delim) {
2361 // Use the nonstandard getdelim()
2362 char * buf = nullptr;
2365 // This looks quadratic but it really depends on realloc
2366 auto const newSize = size + 128;
2367 buf = static_cast<char*>(checkedRealloc(buf, newSize));
2368 is.getline(buf + size, newSize - size, delim);
2369 if (is.bad() || is.eof() || !is.fail()) {
2370 // done by either failure, end of file, or normal read
2371 size += std::strlen(buf + size);
2374 // Here we have failed due to too short a buffer
2375 // Minus one to discount the terminating '\0'
2377 assert(buf[size] == 0);
2378 // Clear the error so we can continue reading
2381 basic_fbstring<E, T, A, S> result(buf, size, size + 1,
2382 AcquireMallocatedString());
2387 template <typename E, class T, class A, class S>
2389 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2390 typename basic_fbstring<E, T, A, S>::traits_type>&
2392 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2393 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2394 basic_fbstring<E, T, A, S>& str) {
2395 // Just forward to the version with a delimiter
2396 return getline(is, str, '\n');
2401 template <typename E1, class T, class A, class S>
2402 const typename basic_fbstring<E1, T, A, S>::size_type
2403 basic_fbstring<E1, T, A, S>::npos =
2404 static_cast<typename basic_fbstring<E1, T, A, S>::size_type>(-1);
2406 #ifndef _LIBSTDCXX_FBSTRING
2407 // basic_string compatibility routines
2409 template <typename E, class T, class A, class S>
2411 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2412 const std::string& rhs) {
2413 return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;
2416 template <typename E, class T, class A, class S>
2418 bool operator==(const std::string& lhs,
2419 const basic_fbstring<E, T, A, S>& rhs) {
2423 template <typename E, class T, class A, class S>
2425 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2426 const std::string& rhs) {
2427 return !(lhs == rhs);
2430 template <typename E, class T, class A, class S>
2432 bool operator!=(const std::string& lhs,
2433 const basic_fbstring<E, T, A, S>& rhs) {
2434 return !(lhs == rhs);
2437 #if !defined(_LIBSTDCXX_FBSTRING)
2438 typedef basic_fbstring<char> fbstring;
2441 // fbstring is relocatable
2442 template <class T, class R, class A, class S>
2443 FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);
2446 _GLIBCXX_END_NAMESPACE_VERSION
2449 } // namespace folly
2451 #ifndef _LIBSTDCXX_FBSTRING
2453 // Hash functions to make fbstring usable with e.g. hash_map
2455 // Handle interaction with different C++ standard libraries, which
2456 // expect these types to be in different namespaces.
2460 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2461 size_t operator()(const folly::basic_fbstring<C> & s) const {
2462 return hash<const C*>::operator()(s.c_str());
2467 struct hash< ::folly::fbstring> {
2468 size_t operator()(const ::folly::fbstring& s) const {
2469 return ::folly::hash::fnv32_buf(s.data(), s.size());
2475 #if defined(_GLIBCXX_SYMVER) && !defined(__BIONIC__)
2476 namespace __gnu_cxx {
2479 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2480 size_t operator()(const folly::basic_fbstring<C> & s) const {
2481 return hash<const C*>::operator()(s.c_str());
2486 struct hash< ::folly::fbstring> {
2487 size_t operator()(const ::folly::fbstring& s) const {
2488 return ::folly::hash::fnv32_buf(s.data(), s.size());
2493 #endif // _GLIBCXX_SYMVER && !__BIONIC__
2494 #endif // _LIBSTDCXX_FBSTRING
2496 #pragma GCC diagnostic pop
2498 #undef FBSTRING_DISABLE_ADDRESS_SANITIZER
2500 #undef FBSTRING_LIKELY
2501 #undef FBSTRING_UNLIKELY
2503 #endif // FOLLY_BASE_FBSTRING_H_