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>
66 // libc++ doesn't provide this header
67 #ifndef _LIBCPP_VERSION
68 // This file appears in two locations: inside fbcode and in the
69 // libstdc++ source code (when embedding fbstring as std::string).
70 // To aid in this schizophrenic use, two macros are defined in
72 // _LIBSTDCXX_FBSTRING - Set inside libstdc++. This is useful to
73 // gate use inside fbcode v. libstdc++
74 #include <bits/c++config.h>
77 #ifdef _LIBSTDCXX_FBSTRING
79 #pragma GCC system_header
81 // Handle the cases where the fbcode version (folly/Malloc.h) is included
82 // either before or after this inclusion.
83 #ifdef FOLLY_MALLOC_H_
84 #undef FOLLY_MALLOC_H_
85 #include "basic_fbstring_malloc.h"
87 #include "basic_fbstring_malloc.h"
88 #undef FOLLY_MALLOC_H_
91 #else // !_LIBSTDCXX_FBSTRING
97 #include "folly/Traits.h"
98 #include "folly/Malloc.h"
99 #include "folly/Hash.h"
101 #ifdef _GLIBCXX_SYMVER
102 #include <ext/hash_set>
103 #include <ext/hash_map>
108 // We defined these here rather than including Likely.h to avoid
109 // redefinition errors when fbstring is imported into libstdc++.
110 #define FBSTRING_LIKELY(x) (__builtin_expect((x), 1))
111 #define FBSTRING_UNLIKELY(x) (__builtin_expect((x), 0))
113 // Ignore shadowing warnings within this file, so includers can use -Wshadow.
114 #pragma GCC diagnostic push
115 #pragma GCC diagnostic ignored "-Wshadow"
117 // FBString cannot use throw when replacing std::string, though it may still
118 // use std::__throw_*
119 #define throw FOLLY_FBSTRING_MAY_NOT_USE_THROW
121 #ifdef _LIBSTDCXX_FBSTRING
122 namespace std _GLIBCXX_VISIBILITY(default) {
123 _GLIBCXX_BEGIN_NAMESPACE_VERSION
128 // Different versions of gcc/clang support different versions of
129 // the address sanitizer attribute. Unfortunately, this attribute
130 // has issues when inlining is used, so disable that as well.
131 #if defined(__clang__)
132 # if __has_feature(address_sanitizer)
133 # if __has_attribute(__no_address_safety_analysis__)
134 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
135 __attribute__((__no_address_safety_analysis__, __noinline__))
136 # elif __has_attribute(__no_sanitize_address__)
137 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
138 __attribute__((__no_sanitize_address__, __noinline__))
141 #elif defined (__GNUC__) && \
143 (__GNUC_MINOR__ >= 8) && \
145 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
146 __attribute__((__no_address_safety_analysis__, __noinline__))
148 #ifndef FBSTRING_DISABLE_ADDRESS_SANITIZER
149 # define FBSTRING_DISABLE_ADDRESS_SANITIZER
152 namespace fbstring_detail {
154 template <class InIt, class OutIt>
157 typename std::iterator_traits<InIt>::difference_type n,
159 for (; n != 0; --n, ++b, ++d) {
165 template <class Pod, class T>
166 inline void pod_fill(Pod* b, Pod* e, T c) {
167 assert(b && e && b <= e);
168 /*static*/ if (sizeof(T) == 1) {
171 auto const ee = b + ((e - b) & ~7u);
172 for (; b != ee; b += 8) {
183 for (; b != e; ++b) {
190 * Lightly structured memcpy, simplifies copying PODs and introduces
191 * some asserts. Unfortunately using this function may cause
192 * measurable overhead (presumably because it adjusts from a begin/end
193 * convention to a pointer/size convention, so it does some extra
194 * arithmetic even though the caller might have done the inverse
195 * adaptation outside).
198 inline void pod_copy(const Pod* b, const Pod* e, Pod* d) {
200 assert(d >= e || d + (e - b) <= b);
201 memcpy(d, b, (e - b) * sizeof(Pod));
205 * Lightly structured memmove, simplifies copying PODs and introduces
209 inline void pod_move(const Pod* b, const Pod* e, Pod* d) {
211 memmove(d, b, (e - b) * sizeof(*b));
214 } // namespace fbstring_detail
217 * Defines a special acquisition method for constructing fbstring
218 * objects. AcquireMallocatedString means that the user passes a
219 * pointer to a malloc-allocated string that the fbstring object will
222 enum class AcquireMallocatedString {};
225 * fbstring_core_model is a mock-up type that defines all required
226 * signatures of a fbstring core. The fbstring class itself uses such
227 * a core object to implement all of the numerous member functions
228 * required by the standard.
230 * If you want to define a new core, copy the definition below and
231 * implement the primitives. Then plug the core into basic_fbstring as
232 * a template argument.
234 template <class Char>
235 class fbstring_core_model {
237 fbstring_core_model();
238 fbstring_core_model(const fbstring_core_model &);
239 ~fbstring_core_model();
240 // Returns a pointer to string's buffer (currently only contiguous
241 // strings are supported). The pointer is guaranteed to be valid
242 // until the next call to a non-const member function.
243 const Char * data() const;
244 // Much like data(), except the string is prepared to support
245 // character-level changes. This call is a signal for
246 // e.g. reference-counted implementation to fork the data. The
247 // pointer is guaranteed to be valid until the next call to a
248 // non-const member function.
249 Char * mutable_data();
250 // Returns a pointer to string's buffer and guarantees that a
251 // readable '\0' lies right after the buffer. The pointer is
252 // guaranteed to be valid until the next call to a non-const member
254 const Char * c_str() const;
255 // Shrinks the string by delta characters. Asserts that delta <=
257 void shrink(size_t delta);
258 // Expands the string by delta characters (i.e. after this call
259 // size() will report the old size() plus delta) but without
260 // initializing the expanded region. Returns a pointer to the memory
261 // to be initialized (the beginning of the expanded portion). The
262 // caller is expected to fill the expanded area appropriately.
263 Char* expand_noinit(size_t delta);
264 // Expands the string by one character and sets the last character
266 void push_back(Char c);
267 // Returns the string's size.
269 // Returns the string's capacity, i.e. maximum size that the string
270 // can grow to without reallocation. Note that for reference counted
271 // strings that's technically a lie - even assigning characters
272 // within the existing size would cause a reallocation.
273 size_t capacity() const;
274 // Returns true if the data underlying the string is actually shared
275 // across multiple strings (in a refcounted fashion).
276 bool isShared() const;
277 // Makes sure that at least minCapacity characters are available for
278 // the string without reallocation. For reference-counted strings,
279 // it should fork the data even if minCapacity < size().
280 void reserve(size_t minCapacity);
283 fbstring_core_model& operator=(const fbstring_core_model &);
288 * gcc-4.7 throws what appears to be some false positive uninitialized
289 * warnings for the members of the MediumLarge struct. So, mute them here.
291 #if defined(__GNUC__) && !defined(__clang__)
292 # pragma GCC diagnostic push
293 # pragma GCC diagnostic ignored "-Wuninitialized"
297 * This is the core of the string. The code should work on 32- and
298 * 64-bit architectures and with any Char size. Porting to big endian
299 * architectures would require some changes.
301 * The storage is selected as follows (assuming we store one-byte
302 * characters on a 64-bit machine): (a) "small" strings between 0 and
303 * 23 chars are stored in-situ without allocation (the rightmost byte
304 * stores the size); (b) "medium" strings from 24 through 254 chars
305 * are stored in malloc-allocated memory that is copied eagerly; (c)
306 * "large" strings of 255 chars and above are stored in a similar
307 * structure as medium arrays, except that the string is
308 * reference-counted and copied lazily. the reference count is
309 * allocated right before the character array.
311 * The discriminator between these three strategies sits in the two
312 * most significant bits of the rightmost char of the storage. If
313 * neither is set, then the string is small (and its length sits in
314 * the lower-order bits of that rightmost character). If the MSb is
315 * set, the string is medium width. If the second MSb is set, then the
318 template <class Char> class fbstring_core {
320 fbstring_core() noexcept {
321 // Only initialize the tag, will set the MSBs (i.e. the small
322 // string size) to zero too
323 ml_.capacity_ = maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)));
324 // or: setSmallSize(0);
326 assert(category() == isSmall && size() == 0);
329 fbstring_core(const fbstring_core & rhs) {
330 assert(&rhs != this);
331 // Simplest case first: small strings are bitblitted
332 if (rhs.category() == isSmall) {
333 assert(offsetof(MediumLarge, data_) == 0);
334 assert(offsetof(MediumLarge, size_) == sizeof(ml_.data_));
335 assert(offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_));
336 const size_t size = rhs.smallSize();
338 ml_.capacity_ = rhs.ml_.capacity_;
341 // Just write the whole thing, don't look at details. In
342 // particular we need to copy capacity anyway because we want
343 // to set the size (don't forget that the last character,
344 // which stores a short string's length, is shared with the
345 // ml_.capacity field).
348 assert(category() == isSmall && this->size() == rhs.size());
349 } else if (rhs.category() == isLarge) {
350 // Large strings are just refcounted
352 RefCounted::incrementRefs(ml_.data_);
353 assert(category() == isLarge && size() == rhs.size());
355 // Medium strings are copied eagerly. Don't forget to allocate
356 // one extra Char for the null terminator.
357 auto const allocSize =
358 goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));
359 ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
360 fbstring_detail::pod_copy(rhs.ml_.data_,
362 rhs.ml_.data_ + rhs.ml_.size_ + 1,
364 // No need for writeTerminator() here, we copied one extra
365 // element just above.
366 ml_.size_ = rhs.ml_.size_;
367 ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
368 assert(category() == isMedium);
370 assert(size() == rhs.size());
371 assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
374 fbstring_core(fbstring_core&& goner) noexcept {
375 if (goner.category() == isSmall) {
376 // Just copy, leave the goner in peace
377 new(this) fbstring_core(goner.small_, goner.smallSize());
381 // Clean goner's carcass
382 goner.setSmallSize(0);
386 // NOTE(agallagher): The word-aligned copy path copies bytes which are
387 // outside the range of the string, and makes address sanitizer unhappy,
388 // so just disable it on this function.
389 fbstring_core(const Char *const data, const size_t size)
390 FBSTRING_DISABLE_ADDRESS_SANITIZER {
391 // Simplest case first: small strings are bitblitted
392 if (size <= maxSmallSize) {
393 // Layout is: Char* data_, size_t size_, size_t capacity_
394 /*static_*/assert(sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t));
395 /*static_*/assert(sizeof(Char*) == sizeof(size_t));
396 // sizeof(size_t) must be a power of 2
397 /*static_*/assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
399 // If data is aligned, use fast word-wise copying. Otherwise,
400 // use conservative memcpy.
401 if (reinterpret_cast<size_t>(data) & (sizeof(size_t) - 1)) {
402 fbstring_detail::pod_copy(data, data + size, small_);
404 // Copy one word (64 bits) at a time
405 const size_t byteSize = size * sizeof(Char);
406 if (byteSize > 2 * sizeof(size_t)) {
408 ml_.capacity_ = reinterpret_cast<const size_t*>(data)[2];
410 ml_.size_ = reinterpret_cast<const size_t*>(data)[1];
412 ml_.data_ = *reinterpret_cast<Char**>(const_cast<Char*>(data));
413 } else if (byteSize > sizeof(size_t)) {
416 } else if (size > 0) {
422 } else if (size <= maxMediumSize) {
423 // Medium strings are allocated normally. Don't forget to
424 // allocate one extra Char for the terminating null.
425 auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));
426 ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
427 fbstring_detail::pod_copy(data, data + size, ml_.data_);
429 ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
431 // Large strings are allocated differently
432 size_t effectiveCapacity = size;
433 auto const newRC = RefCounted::create(data, & effectiveCapacity);
434 ml_.data_ = newRC->data_;
436 ml_.capacity_ = effectiveCapacity | isLarge;
439 assert(this->size() == size);
440 assert(memcmp(this->data(), data, size * sizeof(Char)) == 0);
443 ~fbstring_core() noexcept {
444 auto const c = category();
452 RefCounted::decrementRefs(ml_.data_);
455 // Snatches a previously mallocated string. The parameter "size"
456 // is the size of the string, and the parameter "allocatedSize"
457 // is the size of the mallocated block. The string must be
458 // \0-terminated, so allocatedSize >= size + 1 and data[size] == '\0'.
460 // So if you want a 2-character string, pass malloc(3) as "data",
461 // pass 2 as "size", and pass 3 as "allocatedSize".
462 fbstring_core(Char * const data,
464 const size_t allocatedSize,
465 AcquireMallocatedString) {
467 assert(allocatedSize >= size + 1);
468 assert(data[size] == '\0');
469 // Use the medium string storage
472 // Don't forget about null terminator
473 ml_.capacity_ = (allocatedSize - 1) | isMedium;
475 // No need for the memory
481 // swap below doesn't test whether &rhs == this (and instead
482 // potentially does extra work) on the premise that the rarity of
483 // that situation actually makes the check more expensive than is
485 void swap(fbstring_core & rhs) {
491 // In C++11 data() and c_str() are 100% equivalent.
492 const Char * data() const {
496 Char * mutable_data() {
497 auto const c = category();
501 assert(c == isMedium || c == isLarge);
502 if (c == isLarge && RefCounted::refs(ml_.data_) > 1) {
504 size_t effectiveCapacity = ml_.capacity();
505 auto const newRC = RefCounted::create(& effectiveCapacity);
506 // If this fails, someone placed the wrong capacity in an
508 assert(effectiveCapacity >= ml_.capacity());
509 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
511 RefCounted::decrementRefs(ml_.data_);
512 ml_.data_ = newRC->data_;
513 // No need to call writeTerminator(), we have + 1 above.
518 const Char * c_str() const {
519 auto const c = category();
520 #ifdef FBSTRING_PERVERSE
522 assert(small_[smallSize()] == TERMINATOR || smallSize() == maxSmallSize
523 || small_[smallSize()] == '\0');
524 small_[smallSize()] = '\0';
527 assert(c == isMedium || c == isLarge);
528 assert(ml_.data_[ml_.size_] == TERMINATOR || ml_.data_[ml_.size_] == '\0');
529 ml_.data_[ml_.size_] = '\0';
530 #elif defined(FBSTRING_CONSERVATIVE)
532 assert(small_[smallSize()] == '\0');
535 assert(c == isMedium || c == isLarge);
536 assert(ml_.data_[ml_.size_] == '\0');
539 small_[smallSize()] = '\0';
542 assert(c == isMedium || c == isLarge);
543 ml_.data_[ml_.size_] = '\0';
548 void shrink(const size_t delta) {
549 if (category() == isSmall) {
550 // Check for underflow
551 assert(delta <= smallSize());
552 setSmallSize(smallSize() - delta);
553 } else if (category() == isMedium || RefCounted::refs(ml_.data_) == 1) {
554 // Medium strings and unique large strings need no special
556 assert(ml_.size_ >= delta);
559 assert(ml_.size_ >= delta);
560 // Shared large string, must make unique. This is because of the
561 // durn terminator must be written, which may trample the shared
564 fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this);
566 // No need to write the terminator.
572 void reserve(size_t minCapacity) {
573 if (category() == isLarge) {
575 if (RefCounted::refs(ml_.data_) > 1) {
576 // We must make it unique regardless; in-place reallocation is
577 // useless if the string is shared. In order to not surprise
578 // people, reserve the new block at current capacity or
579 // more. That way, a string's capacity never shrinks after a
581 minCapacity = std::max(minCapacity, ml_.capacity());
582 auto const newRC = RefCounted::create(& minCapacity);
583 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
585 // Done with the old data. No need to call writeTerminator(),
586 // we have + 1 above.
587 RefCounted::decrementRefs(ml_.data_);
588 ml_.data_ = newRC->data_;
589 ml_.capacity_ = minCapacity | isLarge;
590 // size remains unchanged
592 // String is not shared, so let's try to realloc (if needed)
593 if (minCapacity > ml_.capacity()) {
594 // Asking for more memory
596 RefCounted::reallocate(ml_.data_, ml_.size_,
597 ml_.capacity(), minCapacity);
598 ml_.data_ = newRC->data_;
599 ml_.capacity_ = minCapacity | isLarge;
602 assert(capacity() >= minCapacity);
604 } else if (category() == isMedium) {
605 // String is not shared
606 if (minCapacity <= ml_.capacity()) {
607 return; // nothing to do, there's enough room
609 if (minCapacity <= maxMediumSize) {
610 // Keep the string at medium size. Don't forget to allocate
611 // one extra Char for the terminating null.
612 size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char));
613 ml_.data_ = static_cast<Char *>(
616 ml_.size_ * sizeof(Char),
617 (ml_.capacity() + 1) * sizeof(Char),
620 ml_.capacity_ = (capacityBytes / sizeof(Char) - 1) | isMedium;
622 // Conversion from medium to large string
623 fbstring_core nascent;
624 // Will recurse to another branch of this function
625 nascent.reserve(minCapacity);
626 nascent.ml_.size_ = ml_.size_;
627 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_,
631 assert(capacity() >= minCapacity);
634 assert(category() == isSmall);
635 if (minCapacity > maxMediumSize) {
637 auto const newRC = RefCounted::create(& minCapacity);
638 auto const size = smallSize();
639 fbstring_detail::pod_copy(small_, small_ + size + 1, newRC->data_);
640 // No need for writeTerminator(), we wrote it above with + 1.
641 ml_.data_ = newRC->data_;
643 ml_.capacity_ = minCapacity | isLarge;
644 assert(capacity() >= minCapacity);
645 } else if (minCapacity > maxSmallSize) {
647 // Don't forget to allocate one extra Char for the terminating null
648 auto const allocSizeBytes =
649 goodMallocSize((1 + minCapacity) * sizeof(Char));
650 auto const data = static_cast<Char*>(checkedMalloc(allocSizeBytes));
651 auto const size = smallSize();
652 fbstring_detail::pod_copy(small_, small_ + size + 1, data);
653 // No need for writeTerminator(), we wrote it above with + 1.
656 ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1) | isMedium;
659 // Nothing to do, everything stays put
662 assert(capacity() >= minCapacity);
665 Char * expand_noinit(const size_t delta) {
666 // Strategy is simple: make room, then change size
667 assert(capacity() >= size());
669 if (category() == isSmall) {
672 if (newSz <= maxSmallSize) {
680 newSz = ml_.size_ + delta;
681 if (newSz > capacity()) {
685 assert(capacity() >= newSz);
686 // Category can't be small - we took care of that above
687 assert(category() == isMedium || category() == isLarge);
690 assert(size() == newSz);
691 return ml_.data_ + sz;
694 void push_back(Char c) {
695 assert(capacity() >= size());
697 if (category() == isSmall) {
699 if (sz < maxSmallSize) {
700 setSmallSize(sz + 1);
705 reserve(maxSmallSize * 2);
708 if (sz == capacity()) { // always true for isShared()
709 reserve(1 + sz * 3 / 2); // ensures not shared
713 assert(capacity() >= sz + 1);
714 // Category can't be small - we took care of that above
715 assert(category() == isMedium || category() == isLarge);
721 size_t size() const {
722 return category() == isSmall ? smallSize() : ml_.size_;
725 size_t capacity() const {
726 switch (category()) {
730 // For large-sized strings, a multi-referenced chunk has no
731 // available capacity. This is because any attempt to append
732 // data would trigger a new allocation.
733 if (RefCounted::refs(ml_.data_) > 1) return ml_.size_;
736 return ml_.capacity();
739 bool isShared() const {
740 return category() == isLarge && RefCounted::refs(ml_.data_) > 1;
743 #ifdef FBSTRING_PERVERSE
744 enum { TERMINATOR = '^' };
746 enum { TERMINATOR = '\0' };
749 void writeTerminator() {
750 #if defined(FBSTRING_PERVERSE) || defined(FBSTRING_CONSERVATIVE)
751 if (category() == isSmall) {
752 const auto s = smallSize();
753 if (s != maxSmallSize) {
754 small_[s] = TERMINATOR;
757 ml_.data_[ml_.size_] = TERMINATOR;
764 fbstring_core & operator=(const fbstring_core & rhs);
771 size_t capacity() const {
772 return capacity_ & capacityExtractMask;
777 std::atomic<size_t> refCount_;
780 static RefCounted * fromData(Char * p) {
781 return static_cast<RefCounted*>(
783 static_cast<unsigned char*>(static_cast<void*>(p))
784 - sizeof(refCount_)));
787 static size_t refs(Char * p) {
788 return fromData(p)->refCount_.load(std::memory_order_acquire);
791 static void incrementRefs(Char * p) {
792 fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel);
795 static void decrementRefs(Char * p) {
796 auto const dis = fromData(p);
797 size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel);
804 static RefCounted * create(size_t * size) {
805 // Don't forget to allocate one extra Char for the terminating
806 // null. In this case, however, one Char is already part of the
808 const size_t allocSize = goodMallocSize(
809 sizeof(RefCounted) + *size * sizeof(Char));
810 auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));
811 result->refCount_.store(1, std::memory_order_release);
812 *size = (allocSize - sizeof(RefCounted)) / sizeof(Char);
816 static RefCounted * create(const Char * data, size_t * size) {
817 const size_t effectiveSize = *size;
818 auto result = create(size);
819 fbstring_detail::pod_copy(data, data + effectiveSize, result->data_);
823 static RefCounted * reallocate(Char *const data,
824 const size_t currentSize,
825 const size_t currentCapacity,
826 const size_t newCapacity) {
827 assert(newCapacity > 0 && newCapacity > currentSize);
828 auto const dis = fromData(data);
829 assert(dis->refCount_.load(std::memory_order_acquire) == 1);
830 // Don't forget to allocate one extra Char for the terminating
831 // null. In this case, however, one Char is already part of the
833 auto result = static_cast<RefCounted*>(
835 sizeof(RefCounted) + currentSize * sizeof(Char),
836 sizeof(RefCounted) + currentCapacity * sizeof(Char),
837 sizeof(RefCounted) + newCapacity * sizeof(Char)));
838 assert(result->refCount_.load(std::memory_order_acquire) == 1);
844 mutable Char small_[sizeof(MediumLarge) / sizeof(Char)];
845 mutable MediumLarge ml_;
849 lastChar = sizeof(MediumLarge) - 1,
850 maxSmallSize = lastChar / sizeof(Char),
851 maxMediumSize = 254 / sizeof(Char), // coincides with the small
852 // bin size in dlmalloc
853 categoryExtractMask = sizeof(size_t) == 4 ? 0xC0000000 : 0xC000000000000000,
854 capacityExtractMask = ~categoryExtractMask,
856 static_assert(!(sizeof(MediumLarge) % sizeof(Char)),
857 "Corrupt memory layout for fbstring.");
861 isMedium = sizeof(size_t) == 4 ? 0x80000000 : 0x8000000000000000,
862 isLarge = sizeof(size_t) == 4 ? 0x40000000 : 0x4000000000000000,
865 Category category() const {
866 // Assumes little endian
867 return static_cast<Category>(ml_.capacity_ & categoryExtractMask);
870 size_t smallSize() const {
871 assert(category() == isSmall &&
872 static_cast<size_t>(small_[maxSmallSize])
873 <= static_cast<size_t>(maxSmallSize));
874 return static_cast<size_t>(maxSmallSize)
875 - static_cast<size_t>(small_[maxSmallSize]);
878 void setSmallSize(size_t s) {
879 // Warning: this should work with uninitialized strings too,
880 // so don't assume anything about the previous value of
881 // small_[maxSmallSize].
882 assert(s <= maxSmallSize);
883 small_[maxSmallSize] = maxSmallSize - s;
887 #if defined(__GNUC__) && !defined(__clang__)
888 # pragma GCC diagnostic pop
891 #ifndef _LIBSTDCXX_FBSTRING
893 * Dummy fbstring core that uses an actual std::string. This doesn't
894 * make any sense - it's just for testing purposes.
896 template <class Char>
897 class dummy_fbstring_core {
899 dummy_fbstring_core() {
901 dummy_fbstring_core(const dummy_fbstring_core& another)
902 : backend_(another.backend_) {
904 dummy_fbstring_core(const Char * s, size_t n)
907 void swap(dummy_fbstring_core & rhs) {
908 backend_.swap(rhs.backend_);
910 const Char * data() const {
911 return backend_.data();
913 Char * mutable_data() {
914 //assert(!backend_.empty());
915 return &*backend_.begin();
917 void shrink(size_t delta) {
918 assert(delta <= size());
919 backend_.resize(size() - delta);
921 Char * expand_noinit(size_t delta) {
922 auto const sz = size();
923 backend_.resize(size() + delta);
924 return backend_.data() + sz;
926 void push_back(Char c) {
927 backend_.push_back(c);
929 size_t size() const {
930 return backend_.size();
932 size_t capacity() const {
933 return backend_.capacity();
935 bool isShared() const {
938 void reserve(size_t minCapacity) {
939 backend_.reserve(minCapacity);
943 std::basic_string<Char> backend_;
945 #endif // !_LIBSTDCXX_FBSTRING
948 * This is the basic_string replacement. For conformity,
949 * basic_fbstring takes the same template parameters, plus the last
950 * one which is the core.
952 #ifdef _LIBSTDCXX_FBSTRING
953 template <typename E, class T, class A, class Storage>
955 template <typename E,
956 class T = std::char_traits<E>,
957 class A = std::allocator<E>,
958 class Storage = fbstring_core<E> >
960 class basic_fbstring {
964 void (*throw_exc)(const char*),
966 if (!condition) throw_exc(msg);
969 bool isSane() const {
972 empty() == (size() == 0) &&
973 empty() == (begin() == end()) &&
974 size() <= max_size() &&
975 capacity() <= max_size() &&
976 size() <= capacity() &&
977 (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0');
981 friend struct Invariant;
984 explicit Invariant(const basic_fbstring& s) : s_(s) {
991 const basic_fbstring& s_;
993 explicit Invariant(const basic_fbstring&) {}
995 Invariant& operator=(const Invariant&);
1000 typedef T traits_type;
1001 typedef typename traits_type::char_type value_type;
1002 typedef A allocator_type;
1003 typedef typename A::size_type size_type;
1004 typedef typename A::difference_type difference_type;
1006 typedef typename A::reference reference;
1007 typedef typename A::const_reference const_reference;
1008 typedef typename A::pointer pointer;
1009 typedef typename A::const_pointer const_pointer;
1011 typedef E* iterator;
1012 typedef const E* const_iterator;
1013 typedef std::reverse_iterator<iterator
1014 #ifdef NO_ITERATOR_TRAITS
1018 typedef std::reverse_iterator<const_iterator
1019 #ifdef NO_ITERATOR_TRAITS
1022 > const_reverse_iterator;
1024 static const size_type npos; // = size_type(-1)
1027 static void procrustes(size_type& n, size_type nmax) {
1028 if (n > nmax) n = nmax;
1032 // C++11 21.4.2 construct/copy/destroy
1033 explicit basic_fbstring(const A& a = A()) noexcept {
1036 basic_fbstring(const basic_fbstring& str)
1037 : store_(str.store_) {
1041 basic_fbstring(basic_fbstring&& goner) noexcept
1042 : store_(std::move(goner.store_)) {
1045 #ifndef _LIBSTDCXX_FBSTRING
1046 // This is defined for compatibility with std::string
1047 /* implicit */ basic_fbstring(const std::string& str)
1048 : store_(str.data(), str.size()) {
1052 basic_fbstring(const basic_fbstring& str, size_type pos,
1053 size_type n = npos, const A& a = A()) {
1054 assign(str, pos, n);
1057 /* implicit */ basic_fbstring(const value_type* s, const A& a = A())
1059 ? traits_type::length(s)
1061 std::__throw_logic_error(
1062 "basic_fbstring: null pointer initializer not valid");
1067 basic_fbstring(const value_type* s, size_type n, const A& a = A())
1071 basic_fbstring(size_type n, value_type c, const A& a = A()) {
1072 auto const data = store_.expand_noinit(n);
1073 fbstring_detail::pod_fill(data, data + n, c);
1074 store_.writeTerminator();
1077 template <class InIt>
1078 basic_fbstring(InIt begin, InIt end,
1079 typename std::enable_if<
1080 !std::is_same<typename std::remove_const<InIt>::type,
1081 value_type*>::value, const A>::type & a = A()) {
1085 // Specialization for const char*, const char*
1086 basic_fbstring(const value_type* b, const value_type* e)
1087 : store_(b, e - b) {
1090 // Nonstandard constructor
1091 basic_fbstring(value_type *s, size_type n, size_type c,
1092 AcquireMallocatedString a)
1093 : store_(s, n, c, a) {
1096 // Construction from initialization list
1097 basic_fbstring(std::initializer_list<value_type> il) {
1098 assign(il.begin(), il.end());
1101 ~basic_fbstring() noexcept {
1104 basic_fbstring& operator=(const basic_fbstring& lhs) {
1105 if (FBSTRING_UNLIKELY(&lhs == this)) {
1108 auto const oldSize = size();
1109 auto const srcSize = lhs.size();
1110 if (capacity() >= srcSize && !store_.isShared()) {
1111 // great, just copy the contents
1112 if (oldSize < srcSize)
1113 store_.expand_noinit(srcSize - oldSize);
1115 store_.shrink(oldSize - srcSize);
1116 assert(size() == srcSize);
1117 fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin());
1118 store_.writeTerminator();
1120 // need to reallocate, so we may as well create a brand new string
1121 basic_fbstring(lhs).swap(*this);
1127 basic_fbstring& operator=(basic_fbstring&& goner) noexcept {
1128 if (FBSTRING_UNLIKELY(&goner == this)) {
1129 // Compatibility with std::basic_string<>,
1130 // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.
1133 // No need of this anymore
1134 this->~basic_fbstring();
1135 // Move the goner into this
1136 new(&store_) fbstring_core<E>(std::move(goner.store_));
1140 #ifndef _LIBSTDCXX_FBSTRING
1141 // Compatibility with std::string
1142 basic_fbstring & operator=(const std::string & rhs) {
1143 return assign(rhs.data(), rhs.size());
1146 // Compatibility with std::string
1147 std::string toStdString() const {
1148 return std::string(data(), size());
1151 // A lot of code in fbcode still uses this method, so keep it here for now.
1152 const basic_fbstring& toStdString() const {
1157 basic_fbstring& operator=(const value_type* s) {
1161 basic_fbstring& operator=(value_type c) {
1163 store_.expand_noinit(1);
1164 } else if (store_.isShared()) {
1165 basic_fbstring(1, c).swap(*this);
1168 store_.shrink(size() - 1);
1170 *store_.mutable_data() = c;
1171 store_.writeTerminator();
1175 basic_fbstring& operator=(std::initializer_list<value_type> il) {
1176 return assign(il.begin(), il.end());
1179 // C++11 21.4.3 iterators:
1180 iterator begin() { return store_.mutable_data(); }
1182 const_iterator begin() const { return store_.data(); }
1184 const_iterator cbegin() const { return begin(); }
1187 return store_.mutable_data() + store_.size();
1190 const_iterator end() const {
1191 return store_.data() + store_.size();
1194 const_iterator cend() const { return end(); }
1196 reverse_iterator rbegin() {
1197 return reverse_iterator(end());
1200 const_reverse_iterator rbegin() const {
1201 return const_reverse_iterator(end());
1204 const_reverse_iterator crbegin() const { return rbegin(); }
1206 reverse_iterator rend() {
1207 return reverse_iterator(begin());
1210 const_reverse_iterator rend() const {
1211 return const_reverse_iterator(begin());
1214 const_reverse_iterator crend() const { return rend(); }
1217 // C++11 21.4.5, element access:
1218 const value_type& front() const { return *begin(); }
1219 const value_type& back() const {
1221 // Should be begin()[size() - 1], but that branches twice
1222 return *(end() - 1);
1224 value_type& front() { return *begin(); }
1225 value_type& back() {
1227 // Should be begin()[size() - 1], but that branches twice
1228 return *(end() - 1);
1235 // C++11 21.4.4 capacity:
1236 size_type size() const { return store_.size(); }
1238 size_type length() const { return size(); }
1240 size_type max_size() const {
1241 return std::numeric_limits<size_type>::max();
1244 void resize(const size_type n, const value_type c = value_type()) {
1245 auto size = this->size();
1247 store_.shrink(size - n);
1249 // Do this in two steps to minimize slack memory copied (see
1251 auto const capacity = this->capacity();
1252 assert(capacity >= size);
1253 if (size < capacity) {
1254 auto delta = std::min(n, capacity) - size;
1255 store_.expand_noinit(delta);
1256 fbstring_detail::pod_fill(begin() + size, end(), c);
1259 store_.writeTerminator();
1264 auto const delta = n - size;
1265 store_.expand_noinit(delta);
1266 fbstring_detail::pod_fill(end() - delta, end(), c);
1267 store_.writeTerminator();
1269 assert(this->size() == n);
1272 size_type capacity() const { return store_.capacity(); }
1274 void reserve(size_type res_arg = 0) {
1275 enforce(res_arg <= max_size(), std::__throw_length_error, "");
1276 store_.reserve(res_arg);
1279 void shrink_to_fit() {
1280 // Shrink only if slack memory is sufficiently large
1281 if (capacity() < size() * 3 / 2) {
1284 basic_fbstring(cbegin(), cend()).swap(*this);
1287 void clear() { resize(0); }
1289 bool empty() const { return size() == 0; }
1291 // C++11 21.4.5 element access:
1292 const_reference operator[](size_type pos) const {
1293 return *(c_str() + pos);
1296 reference operator[](size_type pos) {
1297 if (pos == size()) {
1298 // Just call c_str() to make sure '\0' is present
1301 return *(begin() + pos);
1304 const_reference at(size_type n) const {
1305 enforce(n <= size(), std::__throw_out_of_range, "");
1309 reference at(size_type n) {
1310 enforce(n < size(), std::__throw_out_of_range, "");
1314 // C++11 21.4.6 modifiers:
1315 basic_fbstring& operator+=(const basic_fbstring& str) {
1319 basic_fbstring& operator+=(const value_type* s) {
1323 basic_fbstring& operator+=(const value_type c) {
1328 basic_fbstring& operator+=(std::initializer_list<value_type> il) {
1333 basic_fbstring& append(const basic_fbstring& str) {
1335 auto desiredSize = size() + str.size();
1337 append(str.data(), str.size());
1338 assert(size() == desiredSize);
1342 basic_fbstring& append(const basic_fbstring& str, const size_type pos,
1344 const size_type sz = str.size();
1345 enforce(pos <= sz, std::__throw_out_of_range, "");
1346 procrustes(n, sz - pos);
1347 return append(str.data() + pos, n);
1350 basic_fbstring& append(const value_type* s, size_type n) {
1352 Invariant checker(*this);
1355 if (FBSTRING_UNLIKELY(!n)) {
1356 // Unlikely but must be done
1359 auto const oldSize = size();
1360 auto const oldData = data();
1361 // Check for aliasing (rare). We could use "<=" here but in theory
1362 // those do not work for pointers unless the pointers point to
1363 // elements in the same array. For that reason we use
1364 // std::less_equal, which is guaranteed to offer a total order
1365 // over pointers. See discussion at http://goo.gl/Cy2ya for more
1367 std::less_equal<const value_type*> le;
1368 if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
1369 assert(le(s + n, oldData + oldSize));
1370 const size_type offset = s - oldData;
1371 store_.reserve(oldSize + n);
1372 // Restore the source
1373 s = data() + offset;
1375 // Warning! Repeated appends with short strings may actually incur
1376 // practically quadratic performance. Avoid that by pushing back
1377 // the first character (which ensures exponential growth) and then
1378 // appending the rest normally. Worst case the append may incur a
1379 // second allocation but that will be rare.
1382 memcpy(store_.expand_noinit(n), s, n * sizeof(value_type));
1383 assert(size() == oldSize + n + 1);
1387 basic_fbstring& append(const value_type* s) {
1388 return append(s, traits_type::length(s));
1391 basic_fbstring& append(size_type n, value_type c) {
1392 resize(size() + n, c);
1396 template<class InputIterator>
1397 basic_fbstring& append(InputIterator first, InputIterator last) {
1398 insert(end(), first, last);
1402 basic_fbstring& append(std::initializer_list<value_type> il) {
1403 return append(il.begin(), il.end());
1406 void push_back(const value_type c) { // primitive
1407 store_.push_back(c);
1410 basic_fbstring& assign(const basic_fbstring& str) {
1411 if (&str == this) return *this;
1412 return assign(str.data(), str.size());
1415 basic_fbstring& assign(basic_fbstring&& str) {
1416 return *this = std::move(str);
1419 basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
1421 const size_type sz = str.size();
1422 enforce(pos <= sz, std::__throw_out_of_range, "");
1423 procrustes(n, sz - pos);
1424 return assign(str.data() + pos, n);
1427 basic_fbstring& assign(const value_type* s, const size_type n) {
1428 Invariant checker(*this);
1431 std::copy(s, s + n, begin());
1433 assert(size() == n);
1435 const value_type *const s2 = s + size();
1436 std::copy(s, s2, begin());
1437 append(s2, n - size());
1438 assert(size() == n);
1440 store_.writeTerminator();
1441 assert(size() == n);
1445 basic_fbstring& assign(const value_type* s) {
1446 return assign(s, traits_type::length(s));
1449 basic_fbstring& assign(std::initializer_list<value_type> il) {
1450 return assign(il.begin(), il.end());
1453 template <class ItOrLength, class ItOrChar>
1454 basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {
1455 return replace(begin(), end(), first_or_n, last_or_c);
1458 basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {
1459 return insert(pos1, str.data(), str.size());
1462 basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
1463 size_type pos2, size_type n) {
1464 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1465 procrustes(n, str.length() - pos2);
1466 return insert(pos1, str.data() + pos2, n);
1469 basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {
1470 enforce(pos <= length(), std::__throw_out_of_range, "");
1471 insert(begin() + pos, s, s + n);
1475 basic_fbstring& insert(size_type pos, const value_type* s) {
1476 return insert(pos, s, traits_type::length(s));
1479 basic_fbstring& insert(size_type pos, size_type n, value_type c) {
1480 enforce(pos <= length(), std::__throw_out_of_range, "");
1481 insert(begin() + pos, n, c);
1485 iterator insert(const_iterator p, const value_type c) {
1486 const size_type pos = p - begin();
1488 return begin() + pos;
1492 template <int i> class Selector {};
1494 iterator insertImplDiscr(const_iterator p,
1495 size_type n, value_type c, Selector<1>) {
1496 Invariant checker(*this);
1498 auto const pos = p - begin();
1499 assert(p >= begin() && p <= end());
1500 if (capacity() - size() < n) {
1501 const size_type sz = p - begin();
1502 reserve(size() + n);
1505 const iterator oldEnd = end();
1506 if (n < size_type(oldEnd - p)) {
1507 append(oldEnd - n, oldEnd);
1509 // reverse_iterator(oldEnd - n),
1510 // reverse_iterator(p),
1511 // reverse_iterator(oldEnd));
1512 fbstring_detail::pod_move(&*p, &*oldEnd - n,
1514 std::fill(begin() + pos, begin() + pos + n, c);
1516 append(n - (end() - p), c);
1517 append(iterator(p), oldEnd);
1518 std::fill(iterator(p), oldEnd, c);
1520 store_.writeTerminator();
1521 return begin() + pos;
1524 template<class InputIter>
1525 iterator insertImplDiscr(const_iterator i,
1526 InputIter b, InputIter e, Selector<0>) {
1527 return insertImpl(i, b, e,
1528 typename std::iterator_traits<InputIter>::iterator_category());
1531 template <class FwdIterator>
1532 iterator insertImpl(const_iterator i,
1533 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1534 Invariant checker(*this);
1536 const size_type pos = i - begin();
1537 const typename std::iterator_traits<FwdIterator>::difference_type n2 =
1538 std::distance(s1, s2);
1540 using namespace fbstring_detail;
1541 assert(pos <= size());
1543 const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
1544 capacity() - size();
1546 // realloc the string
1547 reserve(size() + n2);
1550 if (pos + n2 <= size()) {
1551 const iterator tailBegin = end() - n2;
1552 store_.expand_noinit(n2);
1553 fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2);
1554 std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i),
1555 reverse_iterator(tailBegin + n2));
1556 std::copy(s1, s2, begin() + pos);
1559 const size_type old_size = size();
1560 std::advance(t, old_size - pos);
1561 const size_t newElems = std::distance(t, s2);
1562 store_.expand_noinit(n2);
1563 std::copy(t, s2, begin() + old_size);
1564 fbstring_detail::pod_copy(data() + pos, data() + old_size,
1565 begin() + old_size + newElems);
1566 std::copy(s1, t, begin() + pos);
1568 store_.writeTerminator();
1569 return begin() + pos;
1572 template <class InputIterator>
1573 iterator insertImpl(const_iterator i,
1574 InputIterator b, InputIterator e,
1575 std::input_iterator_tag) {
1576 const auto pos = i - begin();
1577 basic_fbstring temp(begin(), i);
1578 for (; b != e; ++b) {
1581 temp.append(i, cend());
1583 return begin() + pos;
1587 template <class ItOrLength, class ItOrChar>
1588 iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
1589 Selector<std::numeric_limits<ItOrLength>::is_specialized> sel;
1590 return insertImplDiscr(p, first_or_n, last_or_c, sel);
1593 iterator insert(const_iterator p, std::initializer_list<value_type> il) {
1594 return insert(p, il.begin(), il.end());
1597 basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
1598 Invariant checker(*this);
1600 enforce(pos <= length(), std::__throw_out_of_range, "");
1601 procrustes(n, length() - pos);
1602 std::copy(begin() + pos + n, end(), begin() + pos);
1603 resize(length() - n);
1607 iterator erase(iterator position) {
1608 const size_type pos(position - begin());
1609 enforce(pos <= size(), std::__throw_out_of_range, "");
1611 return begin() + pos;
1614 iterator erase(iterator first, iterator last) {
1615 const size_type pos(first - begin());
1616 erase(pos, last - first);
1617 return begin() + pos;
1620 // Replaces at most n1 chars of *this, starting with pos1 with the
1622 basic_fbstring& replace(size_type pos1, size_type n1,
1623 const basic_fbstring& str) {
1624 return replace(pos1, n1, str.data(), str.size());
1627 // Replaces at most n1 chars of *this, starting with pos1,
1628 // with at most n2 chars of str starting with pos2
1629 basic_fbstring& replace(size_type pos1, size_type n1,
1630 const basic_fbstring& str,
1631 size_type pos2, size_type n2) {
1632 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1633 return replace(pos1, n1, str.data() + pos2,
1634 std::min(n2, str.size() - pos2));
1637 // Replaces at most n1 chars of *this, starting with pos, with chars from s
1638 basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
1639 return replace(pos, n1, s, traits_type::length(s));
1642 // Replaces at most n1 chars of *this, starting with pos, with n2
1645 // consolidated with
1647 // Replaces at most n1 chars of *this, starting with pos, with at
1648 // most n2 chars of str. str must have at least n2 chars.
1649 template <class StrOrLength, class NumOrChar>
1650 basic_fbstring& replace(size_type pos, size_type n1,
1651 StrOrLength s_or_n2, NumOrChar n_or_c) {
1652 Invariant checker(*this);
1654 enforce(pos <= size(), std::__throw_out_of_range, "");
1655 procrustes(n1, length() - pos);
1656 const iterator b = begin() + pos;
1657 return replace(b, b + n1, s_or_n2, n_or_c);
1660 basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {
1661 return replace(i1, i2, str.data(), str.length());
1664 basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
1665 return replace(i1, i2, s, traits_type::length(s));
1669 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1670 const value_type* s, size_type n,
1673 assert(begin() <= i1 && i1 <= end());
1674 assert(begin() <= i2 && i2 <= end());
1675 return replace(i1, i2, s, s + n);
1678 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1679 size_type n2, value_type c, Selector<1>) {
1680 const size_type n1 = i2 - i1;
1682 std::fill(i1, i1 + n2, c);
1685 std::fill(i1, i2, c);
1686 insert(i2, n2 - n1, c);
1692 template <class InputIter>
1693 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1694 InputIter b, InputIter e,
1696 replaceImpl(i1, i2, b, e,
1697 typename std::iterator_traits<InputIter>::iterator_category());
1702 template <class FwdIterator>
1703 bool replaceAliased(iterator i1, iterator i2,
1704 FwdIterator s1, FwdIterator s2, std::false_type) {
1708 template <class FwdIterator>
1709 bool replaceAliased(iterator i1, iterator i2,
1710 FwdIterator s1, FwdIterator s2, std::true_type) {
1711 static const std::less_equal<const value_type*> le =
1712 std::less_equal<const value_type*>();
1713 const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());
1717 // Aliased replace, copy to new string
1718 basic_fbstring temp;
1719 temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));
1720 temp.append(begin(), i1).append(s1, s2).append(i2, end());
1725 template <class FwdIterator>
1726 void replaceImpl(iterator i1, iterator i2,
1727 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1728 Invariant checker(*this);
1731 // Handle aliased replace
1732 if (replaceAliased(i1, i2, s1, s2,
1733 std::integral_constant<bool,
1734 std::is_same<FwdIterator, iterator>::value ||
1735 std::is_same<FwdIterator, const_iterator>::value>())) {
1739 auto const n1 = i2 - i1;
1741 auto const n2 = std::distance(s1, s2);
1746 std::copy(s1, s2, i1);
1750 fbstring_detail::copy_n(s1, n1, i1);
1751 std::advance(s1, n1);
1757 template <class InputIterator>
1758 void replaceImpl(iterator i1, iterator i2,
1759 InputIterator b, InputIterator e, std::input_iterator_tag) {
1760 basic_fbstring temp(begin(), i1);
1761 temp.append(b, e).append(i2, end());
1766 template <class T1, class T2>
1767 basic_fbstring& replace(iterator i1, iterator i2,
1768 T1 first_or_n_or_s, T2 last_or_c_or_n) {
1770 num1 = std::numeric_limits<T1>::is_specialized,
1771 num2 = std::numeric_limits<T2>::is_specialized;
1772 return replaceImplDiscr(
1773 i1, i2, first_or_n_or_s, last_or_c_or_n,
1774 Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>());
1777 size_type copy(value_type* s, size_type n, size_type pos = 0) const {
1778 enforce(pos <= size(), std::__throw_out_of_range, "");
1779 procrustes(n, size() - pos);
1781 fbstring_detail::pod_copy(
1788 void swap(basic_fbstring& rhs) {
1789 store_.swap(rhs.store_);
1792 const value_type* c_str() const {
1793 return store_.c_str();
1796 const value_type* data() const { return c_str(); }
1798 allocator_type get_allocator() const {
1799 return allocator_type();
1802 size_type find(const basic_fbstring& str, size_type pos = 0) const {
1803 return find(str.data(), pos, str.length());
1806 size_type find(const value_type* needle, const size_type pos,
1807 const size_type nsize) const {
1808 if (!nsize) return pos;
1809 auto const size = this->size();
1810 // nsize + pos can overflow (eg pos == npos), guard against that by checking
1811 // that nsize + pos does not wrap around.
1812 if (nsize + pos > size || nsize + pos < pos) return npos;
1813 // Don't use std::search, use a Boyer-Moore-like trick by comparing
1814 // the last characters first
1815 auto const haystack = data();
1816 auto const nsize_1 = nsize - 1;
1817 auto const lastNeedle = needle[nsize_1];
1819 // Boyer-Moore skip value for the last char in the needle. Zero is
1820 // not a valid value; skip will be computed the first time it's
1824 const E * i = haystack + pos;
1825 auto iEnd = haystack + size - nsize_1;
1828 // Boyer-Moore: match the last element in the needle
1829 while (i[nsize_1] != lastNeedle) {
1835 // Here we know that the last char matches
1836 // Continue in pedestrian mode
1837 for (size_t j = 0; ; ) {
1839 if (i[j] != needle[j]) {
1840 // Not found, we can skip
1841 // Compute the skip value lazily
1844 while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {
1851 // Check if done searching
1854 return i - haystack;
1861 size_type find(const value_type* s, size_type pos = 0) const {
1862 return find(s, pos, traits_type::length(s));
1865 size_type find (value_type c, size_type pos = 0) const {
1866 return find(&c, pos, 1);
1869 size_type rfind(const basic_fbstring& str, size_type pos = npos) const {
1870 return rfind(str.data(), pos, str.length());
1873 size_type rfind(const value_type* s, size_type pos, size_type n) const {
1874 if (n > length()) return npos;
1875 pos = std::min(pos, length() - n);
1876 if (n == 0) return pos;
1878 const_iterator i(begin() + pos);
1880 if (traits_type::eq(*i, *s)
1881 && traits_type::compare(&*i, s, n) == 0) {
1884 if (i == begin()) break;
1889 size_type rfind(const value_type* s, size_type pos = npos) const {
1890 return rfind(s, pos, traits_type::length(s));
1893 size_type rfind(value_type c, size_type pos = npos) const {
1894 return rfind(&c, pos, 1);
1897 size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {
1898 return find_first_of(str.data(), pos, str.length());
1901 size_type find_first_of(const value_type* s,
1902 size_type pos, size_type n) const {
1903 if (pos > length() || n == 0) return npos;
1904 const_iterator i(begin() + pos),
1906 for (; i != finish; ++i) {
1907 if (traits_type::find(s, n, *i) != 0) {
1914 size_type find_first_of(const value_type* s, size_type pos = 0) const {
1915 return find_first_of(s, pos, traits_type::length(s));
1918 size_type find_first_of(value_type c, size_type pos = 0) const {
1919 return find_first_of(&c, pos, 1);
1922 size_type find_last_of (const basic_fbstring& str,
1923 size_type pos = npos) const {
1924 return find_last_of(str.data(), pos, str.length());
1927 size_type find_last_of (const value_type* s, size_type pos,
1928 size_type n) const {
1929 if (!empty() && n > 0) {
1930 pos = std::min(pos, length() - 1);
1931 const_iterator i(begin() + pos);
1933 if (traits_type::find(s, n, *i) != 0) {
1936 if (i == begin()) break;
1942 size_type find_last_of (const value_type* s,
1943 size_type pos = npos) const {
1944 return find_last_of(s, pos, traits_type::length(s));
1947 size_type find_last_of (value_type c, size_type pos = npos) const {
1948 return find_last_of(&c, pos, 1);
1951 size_type find_first_not_of(const basic_fbstring& str,
1952 size_type pos = 0) const {
1953 return find_first_not_of(str.data(), pos, str.size());
1956 size_type find_first_not_of(const value_type* s, size_type pos,
1957 size_type n) const {
1958 if (pos < length()) {
1962 for (; i != finish; ++i) {
1963 if (traits_type::find(s, n, *i) == 0) {
1971 size_type find_first_not_of(const value_type* s,
1972 size_type pos = 0) const {
1973 return find_first_not_of(s, pos, traits_type::length(s));
1976 size_type find_first_not_of(value_type c, size_type pos = 0) const {
1977 return find_first_not_of(&c, pos, 1);
1980 size_type find_last_not_of(const basic_fbstring& str,
1981 size_type pos = npos) const {
1982 return find_last_not_of(str.data(), pos, str.length());
1985 size_type find_last_not_of(const value_type* s, size_type pos,
1986 size_type n) const {
1987 if (!this->empty()) {
1988 pos = std::min(pos, size() - 1);
1989 const_iterator i(begin() + pos);
1991 if (traits_type::find(s, n, *i) == 0) {
1994 if (i == begin()) break;
2000 size_type find_last_not_of(const value_type* s,
2001 size_type pos = npos) const {
2002 return find_last_not_of(s, pos, traits_type::length(s));
2005 size_type find_last_not_of (value_type c, size_type pos = npos) const {
2006 return find_last_not_of(&c, pos, 1);
2009 basic_fbstring substr(size_type pos = 0, size_type n = npos) const {
2010 enforce(pos <= size(), std::__throw_out_of_range, "");
2011 return basic_fbstring(data() + pos, std::min(n, size() - pos));
2014 int compare(const basic_fbstring& str) const {
2015 // FIX due to Goncalo N M de Carvalho July 18, 2005
2016 return compare(0, size(), str);
2019 int compare(size_type pos1, size_type n1,
2020 const basic_fbstring& str) const {
2021 return compare(pos1, n1, str.data(), str.size());
2024 int compare(size_type pos1, size_type n1,
2025 const value_type* s) const {
2026 return compare(pos1, n1, s, traits_type::length(s));
2029 int compare(size_type pos1, size_type n1,
2030 const value_type* s, size_type n2) const {
2031 enforce(pos1 <= size(), std::__throw_out_of_range, "");
2032 procrustes(n1, size() - pos1);
2033 // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!
2034 const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));
2035 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2038 int compare(size_type pos1, size_type n1,
2039 const basic_fbstring& str,
2040 size_type pos2, size_type n2) const {
2041 enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
2042 return compare(pos1, n1, str.data() + pos2,
2043 std::min(n2, str.size() - pos2));
2046 // Code from Jean-Francois Bastien (03/26/2007)
2047 int compare(const value_type* s) const {
2048 // Could forward to compare(0, size(), s, traits_type::length(s))
2049 // but that does two extra checks
2050 const size_type n1(size()), n2(traits_type::length(s));
2051 const int r = traits_type::compare(data(), s, std::min(n1, n2));
2052 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2060 // non-member functions
2062 template <typename E, class T, class A, class S>
2064 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2065 const basic_fbstring<E, T, A, S>& rhs) {
2067 basic_fbstring<E, T, A, S> result;
2068 result.reserve(lhs.size() + rhs.size());
2069 result.append(lhs).append(rhs);
2070 return std::move(result);
2074 template <typename E, class T, class A, class S>
2076 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2077 const basic_fbstring<E, T, A, S>& rhs) {
2078 return std::move(lhs.append(rhs));
2082 template <typename E, class T, class A, class S>
2084 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2085 basic_fbstring<E, T, A, S>&& rhs) {
2086 if (rhs.capacity() >= lhs.size() + rhs.size()) {
2087 // Good, at least we don't need to reallocate
2088 return std::move(rhs.insert(0, lhs));
2090 // Meh, no go. Forward to operator+(const&, const&).
2091 auto const& rhsC = rhs;
2096 template <typename E, class T, class A, class S>
2098 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2099 basic_fbstring<E, T, A, S>&& rhs) {
2100 return std::move(lhs.append(rhs));
2103 template <typename E, class T, class A, class S>
2105 basic_fbstring<E, T, A, S> operator+(
2106 const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2107 const basic_fbstring<E, T, A, S>& rhs) {
2109 basic_fbstring<E, T, A, S> result;
2110 const typename basic_fbstring<E, T, A, S>::size_type len =
2111 basic_fbstring<E, T, A, S>::traits_type::length(lhs);
2112 result.reserve(len + rhs.size());
2113 result.append(lhs, len).append(rhs);
2117 template <typename E, class T, class A, class S>
2119 basic_fbstring<E, T, A, S> operator+(
2120 typename basic_fbstring<E, T, A, S>::value_type lhs,
2121 const basic_fbstring<E, T, A, S>& rhs) {
2123 basic_fbstring<E, T, A, S> result;
2124 result.reserve(1 + rhs.size());
2125 result.push_back(lhs);
2130 template <typename E, class T, class A, class S>
2132 basic_fbstring<E, T, A, S> operator+(
2133 const basic_fbstring<E, T, A, S>& lhs,
2134 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2136 typedef typename basic_fbstring<E, T, A, S>::size_type size_type;
2137 typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;
2139 basic_fbstring<E, T, A, S> result;
2140 const size_type len = traits_type::length(rhs);
2141 result.reserve(lhs.size() + len);
2142 result.append(lhs).append(rhs, len);
2146 template <typename E, class T, class A, class S>
2148 basic_fbstring<E, T, A, S> operator+(
2149 const basic_fbstring<E, T, A, S>& lhs,
2150 typename basic_fbstring<E, T, A, S>::value_type rhs) {
2152 basic_fbstring<E, T, A, S> result;
2153 result.reserve(lhs.size() + 1);
2155 result.push_back(rhs);
2159 template <typename E, class T, class A, class S>
2161 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2162 const basic_fbstring<E, T, A, S>& rhs) {
2163 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; }
2165 template <typename E, class T, class A, class S>
2167 bool operator==(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2168 const basic_fbstring<E, T, A, S>& rhs) {
2169 return rhs == lhs; }
2171 template <typename E, class T, class A, class S>
2173 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2174 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2175 return lhs.compare(rhs) == 0; }
2177 template <typename E, class T, class A, class S>
2179 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2180 const basic_fbstring<E, T, A, S>& rhs) {
2181 return !(lhs == rhs); }
2183 template <typename E, class T, class A, class S>
2185 bool operator!=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2186 const basic_fbstring<E, T, A, S>& rhs) {
2187 return !(lhs == rhs); }
2189 template <typename E, class T, class A, class S>
2191 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2192 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2193 return !(lhs == rhs); }
2195 template <typename E, class T, class A, class S>
2197 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2198 const basic_fbstring<E, T, A, S>& rhs) {
2199 return lhs.compare(rhs) < 0; }
2201 template <typename E, class T, class A, class S>
2203 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2204 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2205 return lhs.compare(rhs) < 0; }
2207 template <typename E, class T, class A, class S>
2209 bool operator<(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2210 const basic_fbstring<E, T, A, S>& rhs) {
2211 return rhs.compare(lhs) > 0; }
2213 template <typename E, class T, class A, class S>
2215 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2216 const basic_fbstring<E, T, A, S>& rhs) {
2219 template <typename E, class T, class A, class S>
2221 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2222 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2225 template <typename E, class T, class A, class S>
2227 bool operator>(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2228 const basic_fbstring<E, T, A, S>& rhs) {
2231 template <typename E, class T, class A, class S>
2233 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2234 const basic_fbstring<E, T, A, S>& rhs) {
2235 return !(rhs < lhs); }
2237 template <typename E, class T, class A, class S>
2239 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2240 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2241 return !(rhs < lhs); }
2243 template <typename E, class T, class A, class S>
2245 bool operator<=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2246 const basic_fbstring<E, T, A, S>& rhs) {
2247 return !(rhs < lhs); }
2249 template <typename E, class T, class A, class S>
2251 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2252 const basic_fbstring<E, T, A, S>& rhs) {
2253 return !(lhs < rhs); }
2255 template <typename E, class T, class A, class S>
2257 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2258 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2259 return !(lhs < rhs); }
2261 template <typename E, class T, class A, class S>
2263 bool operator>=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2264 const basic_fbstring<E, T, A, S>& rhs) {
2265 return !(lhs < rhs);
2269 template <typename E, class T, class A, class S>
2270 void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {
2274 // TODO: make this faster.
2275 template <typename E, class T, class A, class S>
2278 typename basic_fbstring<E, T, A, S>::value_type,
2279 typename basic_fbstring<E, T, A, S>::traits_type>&
2281 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2282 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2283 basic_fbstring<E, T, A, S>& str) {
2284 typename std::basic_istream<E, T>::sentry sentry(is);
2285 typedef std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2286 typename basic_fbstring<E, T, A, S>::traits_type>
2288 typedef typename __istream_type::ios_base __ios_base;
2289 size_t extracted = 0;
2290 auto err = __ios_base::goodbit;
2292 auto n = is.width();
2297 auto got = is.rdbuf()->sgetc();
2298 for (; extracted != n && got != T::eof() && !isspace(got); ++extracted) {
2299 // Whew. We get to store this guy
2301 got = is.rdbuf()->snextc();
2303 if (got == T::eof()) {
2304 err |= __ios_base::eofbit;
2309 err |= __ios_base::failbit;
2317 template <typename E, class T, class A, class S>
2319 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2320 typename basic_fbstring<E, T, A, S>::traits_type>&
2322 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2323 typename basic_fbstring<E, T, A, S>::traits_type>& os,
2324 const basic_fbstring<E, T, A, S>& str) {
2326 typename std::basic_ostream<
2327 typename basic_fbstring<E, T, A, S>::value_type,
2328 typename basic_fbstring<E, T, A, S>::traits_type>::sentry __s(os);
2330 typedef std::ostreambuf_iterator<
2331 typename basic_fbstring<E, T, A, S>::value_type,
2332 typename basic_fbstring<E, T, A, S>::traits_type> _Ip;
2333 size_t __len = str.size();
2335 (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
2336 if (__pad_and_output(_Ip(os),
2338 __left ? str.data() + __len : str.data(),
2341 os.fill()).failed()) {
2342 os.setstate(std::ios_base::badbit | std::ios_base::failbit);
2346 std::__ostream_insert(os, str.data(), str.size());
2351 #ifndef _LIBSTDCXX_FBSTRING
2353 template <typename E, class T, class A, class S>
2355 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2356 typename basic_fbstring<E, T, A, S>::traits_type>&
2358 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2359 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2360 basic_fbstring<E, T, A, S>& str,
2361 typename basic_fbstring<E, T, A, S>::value_type delim) {
2362 // Use the nonstandard getdelim()
2363 char * buf = nullptr;
2366 // This looks quadratic but it really depends on realloc
2367 auto const newSize = size + 128;
2368 buf = static_cast<char*>(checkedRealloc(buf, newSize));
2369 is.getline(buf + size, newSize - size, delim);
2370 if (is.bad() || is.eof() || !is.fail()) {
2371 // done by either failure, end of file, or normal read
2372 size += std::strlen(buf + size);
2375 // Here we have failed due to too short a buffer
2376 // Minus one to discount the terminating '\0'
2378 assert(buf[size] == 0);
2379 // Clear the error so we can continue reading
2382 basic_fbstring<E, T, A, S> result(buf, size, size + 1,
2383 AcquireMallocatedString());
2388 template <typename E, class T, class A, class S>
2390 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2391 typename basic_fbstring<E, T, A, S>::traits_type>&
2393 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2394 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2395 basic_fbstring<E, T, A, S>& str) {
2396 // Just forward to the version with a delimiter
2397 return getline(is, str, '\n');
2402 template <typename E1, class T, class A, class S>
2403 const typename basic_fbstring<E1, T, A, S>::size_type
2404 basic_fbstring<E1, T, A, S>::npos =
2405 static_cast<typename basic_fbstring<E1, T, A, S>::size_type>(-1);
2407 #ifndef _LIBSTDCXX_FBSTRING
2408 // basic_string compatibility routines
2410 template <typename E, class T, class A, class S>
2412 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2413 const std::string& rhs) {
2414 return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;
2417 template <typename E, class T, class A, class S>
2419 bool operator==(const std::string& lhs,
2420 const basic_fbstring<E, T, A, S>& rhs) {
2424 template <typename E, class T, class A, class S>
2426 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2427 const std::string& rhs) {
2428 return !(lhs == rhs);
2431 template <typename E, class T, class A, class S>
2433 bool operator!=(const std::string& lhs,
2434 const basic_fbstring<E, T, A, S>& rhs) {
2435 return !(lhs == rhs);
2438 #if !defined(_LIBSTDCXX_FBSTRING)
2439 typedef basic_fbstring<char> fbstring;
2442 // fbstring is relocatable
2443 template <class T, class R, class A, class S>
2444 FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);
2447 _GLIBCXX_END_NAMESPACE_VERSION
2450 } // namespace folly
2452 #ifndef _LIBSTDCXX_FBSTRING
2454 // Hash functions to make fbstring usable with e.g. hash_map
2456 // Handle interaction with different C++ standard libraries, which
2457 // expect these types to be in different namespaces.
2461 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2462 size_t operator()(const folly::basic_fbstring<C> & s) const {
2463 return hash<const C*>::operator()(s.c_str());
2468 struct hash< ::folly::fbstring> {
2469 size_t operator()(const ::folly::fbstring& s) const {
2470 return ::folly::hash::fnv32_buf(s.data(), s.size());
2476 #if defined(_GLIBCXX_SYMVER) && !defined(__BIONIC__)
2477 namespace __gnu_cxx {
2480 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2481 size_t operator()(const folly::basic_fbstring<C> & s) const {
2482 return hash<const C*>::operator()(s.c_str());
2487 struct hash< ::folly::fbstring> {
2488 size_t operator()(const ::folly::fbstring& s) const {
2489 return ::folly::hash::fnv32_buf(s.data(), s.size());
2494 #endif // _GLIBCXX_SYMVER && !__BIONIC__
2495 #endif // _LIBSTDCXX_FBSTRING
2497 #pragma GCC diagnostic pop
2499 #undef FBSTRING_DISABLE_ADDRESS_SANITIZER
2501 #undef FBSTRING_LIKELY
2502 #undef FBSTRING_UNLIKELY
2504 #endif // FOLLY_BASE_FBSTRING_H_