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 _GLIBCXX_SYMVER
77 #include <ext/hash_set>
78 #include <ext/hash_map>
81 #ifdef _LIBSTDCXX_FBSTRING
83 #pragma GCC system_header
85 // Handle the cases where the fbcode version (folly/Malloc.h) is included
86 // either before or after this inclusion.
87 #ifdef FOLLY_MALLOC_H_
88 #undef FOLLY_MALLOC_H_
89 #include "basic_fbstring_malloc.h"
91 #include "basic_fbstring_malloc.h"
92 #undef FOLLY_MALLOC_H_
95 #else // !_LIBSTDCXX_FBSTRING
101 #include "folly/Traits.h"
102 #include "folly/Malloc.h"
103 #include "folly/Hash.h"
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) {
159 assert((const void*)&*d != &*b);
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 && small_[maxSmallSize] <= maxSmallSize);
872 return static_cast<size_t>(maxSmallSize)
873 - static_cast<size_t>(small_[maxSmallSize]);
876 void setSmallSize(size_t s) {
877 // Warning: this should work with uninitialized strings too,
878 // so don't assume anything about the previous value of
879 // small_[maxSmallSize].
880 assert(s <= maxSmallSize);
881 small_[maxSmallSize] = maxSmallSize - s;
885 #if defined(__GNUC__) && !defined(__clang__)
886 # pragma GCC diagnostic pop
889 #ifndef _LIBSTDCXX_FBSTRING
891 * Dummy fbstring core that uses an actual std::string. This doesn't
892 * make any sense - it's just for testing purposes.
894 template <class Char>
895 class dummy_fbstring_core {
897 dummy_fbstring_core() {
899 dummy_fbstring_core(const dummy_fbstring_core& another)
900 : backend_(another.backend_) {
902 dummy_fbstring_core(const Char * s, size_t n)
905 void swap(dummy_fbstring_core & rhs) {
906 backend_.swap(rhs.backend_);
908 const Char * data() const {
909 return backend_.data();
911 Char * mutable_data() {
912 //assert(!backend_.empty());
913 return &*backend_.begin();
915 void shrink(size_t delta) {
916 assert(delta <= size());
917 backend_.resize(size() - delta);
919 Char * expand_noinit(size_t delta) {
920 auto const sz = size();
921 backend_.resize(size() + delta);
922 return backend_.data() + sz;
924 void push_back(Char c) {
925 backend_.push_back(c);
927 size_t size() const {
928 return backend_.size();
930 size_t capacity() const {
931 return backend_.capacity();
933 bool isShared() const {
936 void reserve(size_t minCapacity) {
937 backend_.reserve(minCapacity);
941 std::basic_string<Char> backend_;
943 #endif // !_LIBSTDCXX_FBSTRING
946 * This is the basic_string replacement. For conformity,
947 * basic_fbstring takes the same template parameters, plus the last
948 * one which is the core.
950 #ifdef _LIBSTDCXX_FBSTRING
951 template <typename E, class T, class A, class Storage>
953 template <typename E,
954 class T = std::char_traits<E>,
955 class A = std::allocator<E>,
956 class Storage = fbstring_core<E> >
958 class basic_fbstring {
962 void (*throw_exc)(const char*),
964 if (!condition) throw_exc(msg);
967 bool isSane() const {
970 empty() == (size() == 0) &&
971 empty() == (begin() == end()) &&
972 size() <= max_size() &&
973 capacity() <= max_size() &&
974 size() <= capacity() &&
975 (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0');
979 friend struct Invariant;
982 explicit Invariant(const basic_fbstring& s) : s_(s) {
989 const basic_fbstring& s_;
991 explicit Invariant(const basic_fbstring&) {}
993 Invariant& operator=(const Invariant&);
998 typedef T traits_type;
999 typedef typename traits_type::char_type value_type;
1000 typedef A allocator_type;
1001 typedef typename A::size_type size_type;
1002 typedef typename A::difference_type difference_type;
1004 typedef typename A::reference reference;
1005 typedef typename A::const_reference const_reference;
1006 typedef typename A::pointer pointer;
1007 typedef typename A::const_pointer const_pointer;
1009 typedef E* iterator;
1010 typedef const E* const_iterator;
1011 typedef std::reverse_iterator<iterator
1012 #ifdef NO_ITERATOR_TRAITS
1016 typedef std::reverse_iterator<const_iterator
1017 #ifdef NO_ITERATOR_TRAITS
1020 > const_reverse_iterator;
1022 static const size_type npos; // = size_type(-1)
1025 static void procrustes(size_type& n, size_type nmax) {
1026 if (n > nmax) n = nmax;
1030 // C++11 21.4.2 construct/copy/destroy
1031 explicit basic_fbstring(const A& a = A()) noexcept {
1034 basic_fbstring(const basic_fbstring& str)
1035 : store_(str.store_) {
1039 basic_fbstring(basic_fbstring&& goner) noexcept
1040 : store_(std::move(goner.store_)) {
1043 #ifndef _LIBSTDCXX_FBSTRING
1044 // This is defined for compatibility with std::string
1045 /* implicit */ basic_fbstring(const std::string& str)
1046 : store_(str.data(), str.size()) {
1050 basic_fbstring(const basic_fbstring& str, size_type pos,
1051 size_type n = npos, const A& a = A()) {
1052 assign(str, pos, n);
1055 /* implicit */ basic_fbstring(const value_type* s, const A& a = A())
1056 : store_(s, s ? traits_type::length(s) : ({
1057 basic_fbstring<char> err = __PRETTY_FUNCTION__;
1058 err += ": null pointer initializer not valid";
1059 std::__throw_logic_error(err.c_str());
1064 basic_fbstring(const value_type* s, size_type n, const A& a = A())
1068 basic_fbstring(size_type n, value_type c, const A& a = A()) {
1069 auto const data = store_.expand_noinit(n);
1070 fbstring_detail::pod_fill(data, data + n, c);
1071 store_.writeTerminator();
1074 template <class InIt>
1075 basic_fbstring(InIt begin, InIt end,
1076 typename std::enable_if<
1077 !std::is_same<typename std::remove_const<InIt>::type,
1078 value_type*>::value, const A>::type & a = A()) {
1082 // Specialization for const char*, const char*
1083 basic_fbstring(const value_type* b, const value_type* e)
1084 : store_(b, e - b) {
1087 // Nonstandard constructor
1088 basic_fbstring(value_type *s, size_type n, size_type c,
1089 AcquireMallocatedString a)
1090 : store_(s, n, c, a) {
1093 // Construction from initialization list
1094 basic_fbstring(std::initializer_list<value_type> il) {
1095 assign(il.begin(), il.end());
1098 ~basic_fbstring() noexcept {
1101 basic_fbstring& operator=(const basic_fbstring& lhs) {
1102 if (FBSTRING_UNLIKELY(&lhs == this)) {
1105 auto const oldSize = size();
1106 auto const srcSize = lhs.size();
1107 if (capacity() >= srcSize && !store_.isShared()) {
1108 // great, just copy the contents
1109 if (oldSize < srcSize)
1110 store_.expand_noinit(srcSize - oldSize);
1112 store_.shrink(oldSize - srcSize);
1113 assert(size() == srcSize);
1114 fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin());
1115 store_.writeTerminator();
1117 // need to reallocate, so we may as well create a brand new string
1118 basic_fbstring(lhs).swap(*this);
1124 basic_fbstring& operator=(basic_fbstring&& goner) noexcept {
1125 if (FBSTRING_UNLIKELY(&goner == this)) {
1126 // Compatibility with std::basic_string<>,
1127 // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.
1130 // No need of this anymore
1131 this->~basic_fbstring();
1132 // Move the goner into this
1133 new(&store_) fbstring_core<E>(std::move(goner.store_));
1137 #ifndef _LIBSTDCXX_FBSTRING
1138 // Compatibility with std::string
1139 basic_fbstring & operator=(const std::string & rhs) {
1140 return assign(rhs.data(), rhs.size());
1143 // Compatibility with std::string
1144 std::string toStdString() const {
1145 return std::string(data(), size());
1148 // A lot of code in fbcode still uses this method, so keep it here for now.
1149 const basic_fbstring& toStdString() const {
1154 basic_fbstring& operator=(const value_type* s) {
1158 basic_fbstring& operator=(value_type c) {
1160 store_.expand_noinit(1);
1161 } else if (store_.isShared()) {
1162 basic_fbstring(1, c).swap(*this);
1165 store_.shrink(size() - 1);
1167 *store_.mutable_data() = c;
1168 store_.writeTerminator();
1172 basic_fbstring& operator=(std::initializer_list<value_type> il) {
1173 return assign(il.begin(), il.end());
1176 // C++11 21.4.3 iterators:
1177 iterator begin() { return store_.mutable_data(); }
1179 const_iterator begin() const { return store_.data(); }
1181 const_iterator cbegin() const { return begin(); }
1184 return store_.mutable_data() + store_.size();
1187 const_iterator end() const {
1188 return store_.data() + store_.size();
1191 const_iterator cend() const { return end(); }
1193 reverse_iterator rbegin() {
1194 return reverse_iterator(end());
1197 const_reverse_iterator rbegin() const {
1198 return const_reverse_iterator(end());
1201 const_reverse_iterator crbegin() const { return rbegin(); }
1203 reverse_iterator rend() {
1204 return reverse_iterator(begin());
1207 const_reverse_iterator rend() const {
1208 return const_reverse_iterator(begin());
1211 const_reverse_iterator crend() const { return rend(); }
1214 // C++11 21.4.5, element access:
1215 const value_type& front() const { return *begin(); }
1216 const value_type& back() const {
1218 // Should be begin()[size() - 1], but that branches twice
1219 return *(end() - 1);
1221 value_type& front() { return *begin(); }
1222 value_type& back() {
1224 // Should be begin()[size() - 1], but that branches twice
1225 return *(end() - 1);
1232 // C++11 21.4.4 capacity:
1233 size_type size() const { return store_.size(); }
1235 size_type length() const { return size(); }
1237 size_type max_size() const {
1238 return std::numeric_limits<size_type>::max();
1241 void resize(const size_type n, const value_type c = value_type()) {
1242 auto size = this->size();
1244 store_.shrink(size - n);
1246 // Do this in two steps to minimize slack memory copied (see
1248 auto const capacity = this->capacity();
1249 assert(capacity >= size);
1250 if (size < capacity) {
1251 auto delta = std::min(n, capacity) - size;
1252 store_.expand_noinit(delta);
1253 fbstring_detail::pod_fill(begin() + size, end(), c);
1256 store_.writeTerminator();
1261 auto const delta = n - size;
1262 store_.expand_noinit(delta);
1263 fbstring_detail::pod_fill(end() - delta, end(), c);
1264 store_.writeTerminator();
1266 assert(this->size() == n);
1269 size_type capacity() const { return store_.capacity(); }
1271 void reserve(size_type res_arg = 0) {
1272 enforce(res_arg <= max_size(), std::__throw_length_error, "");
1273 store_.reserve(res_arg);
1276 void shrink_to_fit() {
1277 // Shrink only if slack memory is sufficiently large
1278 if (capacity() < size() * 3 / 2) {
1281 basic_fbstring(cbegin(), cend()).swap(*this);
1284 void clear() { resize(0); }
1286 bool empty() const { return size() == 0; }
1288 // C++11 21.4.5 element access:
1289 const_reference operator[](size_type pos) const {
1290 return *(c_str() + pos);
1293 reference operator[](size_type pos) {
1294 if (pos == size()) {
1295 // Just call c_str() to make sure '\0' is present
1298 return *(begin() + pos);
1301 const_reference at(size_type n) const {
1302 enforce(n <= size(), std::__throw_out_of_range, "");
1306 reference at(size_type n) {
1307 enforce(n < size(), std::__throw_out_of_range, "");
1311 // C++11 21.4.6 modifiers:
1312 basic_fbstring& operator+=(const basic_fbstring& str) {
1316 basic_fbstring& operator+=(const value_type* s) {
1320 basic_fbstring& operator+=(const value_type c) {
1325 basic_fbstring& operator+=(std::initializer_list<value_type> il) {
1330 basic_fbstring& append(const basic_fbstring& str) {
1332 auto desiredSize = size() + str.size();
1334 append(str.data(), str.size());
1335 assert(size() == desiredSize);
1339 basic_fbstring& append(const basic_fbstring& str, const size_type pos,
1341 const size_type sz = str.size();
1342 enforce(pos <= sz, std::__throw_out_of_range, "");
1343 procrustes(n, sz - pos);
1344 return append(str.data() + pos, n);
1347 basic_fbstring& append(const value_type* s, size_type n) {
1349 Invariant checker(*this);
1352 if (FBSTRING_UNLIKELY(!n)) {
1353 // Unlikely but must be done
1356 auto const oldSize = size();
1357 auto const oldData = data();
1358 // Check for aliasing (rare). We could use "<=" here but in theory
1359 // those do not work for pointers unless the pointers point to
1360 // elements in the same array. For that reason we use
1361 // std::less_equal, which is guaranteed to offer a total order
1362 // over pointers. See discussion at http://goo.gl/Cy2ya for more
1364 std::less_equal<const value_type*> le;
1365 if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
1366 assert(le(s + n, oldData + oldSize));
1367 const size_type offset = s - oldData;
1368 store_.reserve(oldSize + n);
1369 // Restore the source
1370 s = data() + offset;
1372 // Warning! Repeated appends with short strings may actually incur
1373 // practically quadratic performance. Avoid that by pushing back
1374 // the first character (which ensures exponential growth) and then
1375 // appending the rest normally. Worst case the append may incur a
1376 // second allocation but that will be rare.
1379 memcpy(store_.expand_noinit(n), s, n * sizeof(value_type));
1380 assert(size() == oldSize + n + 1);
1384 basic_fbstring& append(const value_type* s) {
1385 return append(s, traits_type::length(s));
1388 basic_fbstring& append(size_type n, value_type c) {
1389 resize(size() + n, c);
1393 template<class InputIterator>
1394 basic_fbstring& append(InputIterator first, InputIterator last) {
1395 insert(end(), first, last);
1399 basic_fbstring& append(std::initializer_list<value_type> il) {
1400 return append(il.begin(), il.end());
1403 void push_back(const value_type c) { // primitive
1404 store_.push_back(c);
1407 basic_fbstring& assign(const basic_fbstring& str) {
1408 if (&str == this) return *this;
1409 return assign(str.data(), str.size());
1412 basic_fbstring& assign(basic_fbstring&& str) {
1413 return *this = std::move(str);
1416 basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
1418 const size_type sz = str.size();
1419 enforce(pos <= sz, std::__throw_out_of_range, "");
1420 procrustes(n, sz - pos);
1421 return assign(str.data() + pos, n);
1424 basic_fbstring& assign(const value_type* s, const size_type n) {
1425 Invariant checker(*this);
1428 std::copy(s, s + n, begin());
1430 assert(size() == n);
1432 const value_type *const s2 = s + size();
1433 std::copy(s, s2, begin());
1434 append(s2, n - size());
1435 assert(size() == n);
1437 store_.writeTerminator();
1438 assert(size() == n);
1442 basic_fbstring& assign(const value_type* s) {
1443 return assign(s, traits_type::length(s));
1446 basic_fbstring& assign(std::initializer_list<value_type> il) {
1447 return assign(il.begin(), il.end());
1450 template <class ItOrLength, class ItOrChar>
1451 basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {
1452 return replace(begin(), end(), first_or_n, last_or_c);
1455 basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {
1456 return insert(pos1, str.data(), str.size());
1459 basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
1460 size_type pos2, size_type n) {
1461 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1462 procrustes(n, str.length() - pos2);
1463 return insert(pos1, str.data() + pos2, n);
1466 basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {
1467 enforce(pos <= length(), std::__throw_out_of_range, "");
1468 insert(begin() + pos, s, s + n);
1472 basic_fbstring& insert(size_type pos, const value_type* s) {
1473 return insert(pos, s, traits_type::length(s));
1476 basic_fbstring& insert(size_type pos, size_type n, value_type c) {
1477 enforce(pos <= length(), std::__throw_out_of_range, "");
1478 insert(begin() + pos, n, c);
1482 iterator insert(const_iterator p, const value_type c) {
1483 const size_type pos = p - begin();
1485 return begin() + pos;
1489 template <int i> class Selector {};
1491 iterator insertImplDiscr(const_iterator p,
1492 size_type n, value_type c, Selector<1>) {
1493 Invariant checker(*this);
1495 auto const pos = p - begin();
1496 assert(p >= begin() && p <= end());
1497 if (capacity() - size() < n) {
1498 const size_type sz = p - begin();
1499 reserve(size() + n);
1502 const iterator oldEnd = end();
1503 if (n < size_type(oldEnd - p)) {
1504 append(oldEnd - n, oldEnd);
1506 // reverse_iterator(oldEnd - n),
1507 // reverse_iterator(p),
1508 // reverse_iterator(oldEnd));
1509 fbstring_detail::pod_move(&*p, &*oldEnd - n,
1511 std::fill(begin() + pos, begin() + pos + n, c);
1513 append(n - (end() - p), c);
1514 append(iterator(p), oldEnd);
1515 std::fill(iterator(p), oldEnd, c);
1517 store_.writeTerminator();
1518 return begin() + pos;
1521 template<class InputIter>
1522 iterator insertImplDiscr(const_iterator i,
1523 InputIter b, InputIter e, Selector<0>) {
1524 return insertImpl(i, b, e,
1525 typename std::iterator_traits<InputIter>::iterator_category());
1528 template <class FwdIterator>
1529 iterator insertImpl(const_iterator i,
1530 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1531 Invariant checker(*this);
1533 const size_type pos = i - begin();
1534 const typename std::iterator_traits<FwdIterator>::difference_type n2 =
1535 std::distance(s1, s2);
1537 using namespace fbstring_detail;
1538 assert(pos <= size());
1540 const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
1541 capacity() - size();
1543 // realloc the string
1544 reserve(size() + n2);
1547 if (pos + n2 <= size()) {
1548 const iterator tailBegin = end() - n2;
1549 store_.expand_noinit(n2);
1550 fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2);
1551 std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i),
1552 reverse_iterator(tailBegin + n2));
1553 std::copy(s1, s2, begin() + pos);
1556 const size_type old_size = size();
1557 std::advance(t, old_size - pos);
1558 const size_t newElems = std::distance(t, s2);
1559 store_.expand_noinit(n2);
1560 std::copy(t, s2, begin() + old_size);
1561 fbstring_detail::pod_copy(data() + pos, data() + old_size,
1562 begin() + old_size + newElems);
1563 std::copy(s1, t, begin() + pos);
1565 store_.writeTerminator();
1566 return begin() + pos;
1569 template <class InputIterator>
1570 iterator insertImpl(const_iterator i,
1571 InputIterator b, InputIterator e,
1572 std::input_iterator_tag) {
1573 const auto pos = i - begin();
1574 basic_fbstring temp(begin(), i);
1575 for (; b != e; ++b) {
1578 temp.append(i, cend());
1580 return begin() + pos;
1584 template <class ItOrLength, class ItOrChar>
1585 iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
1586 Selector<std::numeric_limits<ItOrLength>::is_specialized> sel;
1587 return insertImplDiscr(p, first_or_n, last_or_c, sel);
1590 iterator insert(const_iterator p, std::initializer_list<value_type> il) {
1591 return insert(p, il.begin(), il.end());
1594 basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
1595 Invariant checker(*this);
1597 enforce(pos <= length(), std::__throw_out_of_range, "");
1598 procrustes(n, length() - pos);
1599 std::copy(begin() + pos + n, end(), begin() + pos);
1600 resize(length() - n);
1604 iterator erase(iterator position) {
1605 const size_type pos(position - begin());
1606 enforce(pos <= size(), std::__throw_out_of_range, "");
1608 return begin() + pos;
1611 iterator erase(iterator first, iterator last) {
1612 const size_type pos(first - begin());
1613 erase(pos, last - first);
1614 return begin() + pos;
1617 // Replaces at most n1 chars of *this, starting with pos1 with the
1619 basic_fbstring& replace(size_type pos1, size_type n1,
1620 const basic_fbstring& str) {
1621 return replace(pos1, n1, str.data(), str.size());
1624 // Replaces at most n1 chars of *this, starting with pos1,
1625 // with at most n2 chars of str starting with pos2
1626 basic_fbstring& replace(size_type pos1, size_type n1,
1627 const basic_fbstring& str,
1628 size_type pos2, size_type n2) {
1629 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1630 return replace(pos1, n1, str.data() + pos2,
1631 std::min(n2, str.size() - pos2));
1634 // Replaces at most n1 chars of *this, starting with pos, with chars from s
1635 basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
1636 return replace(pos, n1, s, traits_type::length(s));
1639 // Replaces at most n1 chars of *this, starting with pos, with n2
1642 // consolidated with
1644 // Replaces at most n1 chars of *this, starting with pos, with at
1645 // most n2 chars of str. str must have at least n2 chars.
1646 template <class StrOrLength, class NumOrChar>
1647 basic_fbstring& replace(size_type pos, size_type n1,
1648 StrOrLength s_or_n2, NumOrChar n_or_c) {
1649 Invariant checker(*this);
1651 enforce(pos <= size(), std::__throw_out_of_range, "");
1652 procrustes(n1, length() - pos);
1653 const iterator b = begin() + pos;
1654 return replace(b, b + n1, s_or_n2, n_or_c);
1657 basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {
1658 return replace(i1, i2, str.data(), str.length());
1661 basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
1662 return replace(i1, i2, s, traits_type::length(s));
1666 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1667 const value_type* s, size_type n,
1670 assert(begin() <= i1 && i1 <= end());
1671 assert(begin() <= i2 && i2 <= end());
1672 return replace(i1, i2, s, s + n);
1675 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1676 size_type n2, value_type c, Selector<1>) {
1677 const size_type n1 = i2 - i1;
1679 std::fill(i1, i1 + n2, c);
1682 std::fill(i1, i2, c);
1683 insert(i2, n2 - n1, c);
1689 template <class InputIter>
1690 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1691 InputIter b, InputIter e,
1693 replaceImpl(i1, i2, b, e,
1694 typename std::iterator_traits<InputIter>::iterator_category());
1699 template <class FwdIterator, class P>
1700 bool replaceAliased(iterator i1, iterator i2,
1701 FwdIterator s1, FwdIterator s2, P*) {
1705 template <class FwdIterator>
1706 bool replaceAliased(iterator i1, iterator i2,
1707 FwdIterator s1, FwdIterator s2, value_type*) {
1708 static const std::less_equal<const value_type*> le =
1709 std::less_equal<const value_type*>();
1710 const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());
1714 // Aliased replace, copy to new string
1715 basic_fbstring temp;
1716 temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));
1717 temp.append(begin(), i1).append(s1, s2).append(i2, end());
1723 template <class FwdIterator>
1724 void replaceImpl(iterator i1, iterator i2,
1725 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1726 Invariant checker(*this);
1729 // Handle aliased replace
1730 if (replaceAliased(i1, i2, s1, s2, &*s1)) {
1734 auto const n1 = i2 - i1;
1736 auto const n2 = std::distance(s1, s2);
1741 std::copy(s1, s2, i1);
1745 fbstring_detail::copy_n(s1, n1, i1);
1746 std::advance(s1, n1);
1752 template <class InputIterator>
1753 void replaceImpl(iterator i1, iterator i2,
1754 InputIterator b, InputIterator e, std::input_iterator_tag) {
1755 basic_fbstring temp(begin(), i1);
1756 temp.append(b, e).append(i2, end());
1761 template <class T1, class T2>
1762 basic_fbstring& replace(iterator i1, iterator i2,
1763 T1 first_or_n_or_s, T2 last_or_c_or_n) {
1765 num1 = std::numeric_limits<T1>::is_specialized,
1766 num2 = std::numeric_limits<T2>::is_specialized;
1767 return replaceImplDiscr(
1768 i1, i2, first_or_n_or_s, last_or_c_or_n,
1769 Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>());
1772 size_type copy(value_type* s, size_type n, size_type pos = 0) const {
1773 enforce(pos <= size(), std::__throw_out_of_range, "");
1774 procrustes(n, size() - pos);
1776 fbstring_detail::pod_copy(
1783 void swap(basic_fbstring& rhs) {
1784 store_.swap(rhs.store_);
1787 const value_type* c_str() const {
1788 return store_.c_str();
1791 const value_type* data() const { return c_str(); }
1793 allocator_type get_allocator() const {
1794 return allocator_type();
1797 size_type find(const basic_fbstring& str, size_type pos = 0) const {
1798 return find(str.data(), pos, str.length());
1801 size_type find(const value_type* needle, const size_type pos,
1802 const size_type nsize) const {
1803 if (!nsize) return pos;
1804 auto const size = this->size();
1805 // nsize + pos can overflow (eg pos == npos), guard against that by checking
1806 // that nsize + pos does not wrap around.
1807 if (nsize + pos > size || nsize + pos < pos) return npos;
1808 // Don't use std::search, use a Boyer-Moore-like trick by comparing
1809 // the last characters first
1810 auto const haystack = data();
1811 auto const nsize_1 = nsize - 1;
1812 auto const lastNeedle = needle[nsize_1];
1814 // Boyer-Moore skip value for the last char in the needle. Zero is
1815 // not a valid value; skip will be computed the first time it's
1819 const E * i = haystack + pos;
1820 auto iEnd = haystack + size - nsize_1;
1823 // Boyer-Moore: match the last element in the needle
1824 while (i[nsize_1] != lastNeedle) {
1830 // Here we know that the last char matches
1831 // Continue in pedestrian mode
1832 for (size_t j = 0; ; ) {
1834 if (i[j] != needle[j]) {
1835 // Not found, we can skip
1836 // Compute the skip value lazily
1839 while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {
1846 // Check if done searching
1849 return i - haystack;
1856 size_type find(const value_type* s, size_type pos = 0) const {
1857 return find(s, pos, traits_type::length(s));
1860 size_type find (value_type c, size_type pos = 0) const {
1861 return find(&c, pos, 1);
1864 size_type rfind(const basic_fbstring& str, size_type pos = npos) const {
1865 return rfind(str.data(), pos, str.length());
1868 size_type rfind(const value_type* s, size_type pos, size_type n) const {
1869 if (n > length()) return npos;
1870 pos = std::min(pos, length() - n);
1871 if (n == 0) return pos;
1873 const_iterator i(begin() + pos);
1875 if (traits_type::eq(*i, *s)
1876 && traits_type::compare(&*i, s, n) == 0) {
1879 if (i == begin()) break;
1884 size_type rfind(const value_type* s, size_type pos = npos) const {
1885 return rfind(s, pos, traits_type::length(s));
1888 size_type rfind(value_type c, size_type pos = npos) const {
1889 return rfind(&c, pos, 1);
1892 size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {
1893 return find_first_of(str.data(), pos, str.length());
1896 size_type find_first_of(const value_type* s,
1897 size_type pos, size_type n) const {
1898 if (pos > length() || n == 0) return npos;
1899 const_iterator i(begin() + pos),
1901 for (; i != finish; ++i) {
1902 if (traits_type::find(s, n, *i) != 0) {
1909 size_type find_first_of(const value_type* s, size_type pos = 0) const {
1910 return find_first_of(s, pos, traits_type::length(s));
1913 size_type find_first_of(value_type c, size_type pos = 0) const {
1914 return find_first_of(&c, pos, 1);
1917 size_type find_last_of (const basic_fbstring& str,
1918 size_type pos = npos) const {
1919 return find_last_of(str.data(), pos, str.length());
1922 size_type find_last_of (const value_type* s, size_type pos,
1923 size_type n) const {
1924 if (!empty() && n > 0) {
1925 pos = std::min(pos, length() - 1);
1926 const_iterator i(begin() + pos);
1928 if (traits_type::find(s, n, *i) != 0) {
1931 if (i == begin()) break;
1937 size_type find_last_of (const value_type* s,
1938 size_type pos = npos) const {
1939 return find_last_of(s, pos, traits_type::length(s));
1942 size_type find_last_of (value_type c, size_type pos = npos) const {
1943 return find_last_of(&c, pos, 1);
1946 size_type find_first_not_of(const basic_fbstring& str,
1947 size_type pos = 0) const {
1948 return find_first_not_of(str.data(), pos, str.size());
1951 size_type find_first_not_of(const value_type* s, size_type pos,
1952 size_type n) const {
1953 if (pos < length()) {
1957 for (; i != finish; ++i) {
1958 if (traits_type::find(s, n, *i) == 0) {
1966 size_type find_first_not_of(const value_type* s,
1967 size_type pos = 0) const {
1968 return find_first_not_of(s, pos, traits_type::length(s));
1971 size_type find_first_not_of(value_type c, size_type pos = 0) const {
1972 return find_first_not_of(&c, pos, 1);
1975 size_type find_last_not_of(const basic_fbstring& str,
1976 size_type pos = npos) const {
1977 return find_last_not_of(str.data(), pos, str.length());
1980 size_type find_last_not_of(const value_type* s, size_type pos,
1981 size_type n) const {
1982 if (!this->empty()) {
1983 pos = std::min(pos, size() - 1);
1984 const_iterator i(begin() + pos);
1986 if (traits_type::find(s, n, *i) == 0) {
1989 if (i == begin()) break;
1995 size_type find_last_not_of(const value_type* s,
1996 size_type pos = npos) const {
1997 return find_last_not_of(s, pos, traits_type::length(s));
2000 size_type find_last_not_of (value_type c, size_type pos = npos) const {
2001 return find_last_not_of(&c, pos, 1);
2004 basic_fbstring substr(size_type pos = 0, size_type n = npos) const {
2005 enforce(pos <= size(), std::__throw_out_of_range, "");
2006 return basic_fbstring(data() + pos, std::min(n, size() - pos));
2009 int compare(const basic_fbstring& str) const {
2010 // FIX due to Goncalo N M de Carvalho July 18, 2005
2011 return compare(0, size(), str);
2014 int compare(size_type pos1, size_type n1,
2015 const basic_fbstring& str) const {
2016 return compare(pos1, n1, str.data(), str.size());
2019 int compare(size_type pos1, size_type n1,
2020 const value_type* s) const {
2021 return compare(pos1, n1, s, traits_type::length(s));
2024 int compare(size_type pos1, size_type n1,
2025 const value_type* s, size_type n2) const {
2026 enforce(pos1 <= size(), std::__throw_out_of_range, "");
2027 procrustes(n1, size() - pos1);
2028 // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!
2029 const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));
2030 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2033 int compare(size_type pos1, size_type n1,
2034 const basic_fbstring& str,
2035 size_type pos2, size_type n2) const {
2036 enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
2037 return compare(pos1, n1, str.data() + pos2,
2038 std::min(n2, str.size() - pos2));
2041 // Code from Jean-Francois Bastien (03/26/2007)
2042 int compare(const value_type* s) const {
2043 // Could forward to compare(0, size(), s, traits_type::length(s))
2044 // but that does two extra checks
2045 const size_type n1(size()), n2(traits_type::length(s));
2046 const int r = traits_type::compare(data(), s, std::min(n1, n2));
2047 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2055 // non-member functions
2057 template <typename E, class T, class A, class S>
2059 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2060 const basic_fbstring<E, T, A, S>& rhs) {
2062 basic_fbstring<E, T, A, S> result;
2063 result.reserve(lhs.size() + rhs.size());
2064 result.append(lhs).append(rhs);
2065 return std::move(result);
2069 template <typename E, class T, class A, class S>
2071 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2072 const basic_fbstring<E, T, A, S>& rhs) {
2073 return std::move(lhs.append(rhs));
2077 template <typename E, class T, class A, class S>
2079 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2080 basic_fbstring<E, T, A, S>&& rhs) {
2081 if (rhs.capacity() >= lhs.size() + rhs.size()) {
2082 // Good, at least we don't need to reallocate
2083 return std::move(rhs.insert(0, lhs));
2085 // Meh, no go. Forward to operator+(const&, const&).
2086 auto const& rhsC = rhs;
2091 template <typename E, class T, class A, class S>
2093 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2094 basic_fbstring<E, T, A, S>&& rhs) {
2095 return std::move(lhs.append(rhs));
2098 template <typename E, class T, class A, class S>
2100 basic_fbstring<E, T, A, S> operator+(
2101 const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2102 const basic_fbstring<E, T, A, S>& rhs) {
2104 basic_fbstring<E, T, A, S> result;
2105 const typename basic_fbstring<E, T, A, S>::size_type len =
2106 basic_fbstring<E, T, A, S>::traits_type::length(lhs);
2107 result.reserve(len + rhs.size());
2108 result.append(lhs, len).append(rhs);
2112 template <typename E, class T, class A, class S>
2114 basic_fbstring<E, T, A, S> operator+(
2115 typename basic_fbstring<E, T, A, S>::value_type lhs,
2116 const basic_fbstring<E, T, A, S>& rhs) {
2118 basic_fbstring<E, T, A, S> result;
2119 result.reserve(1 + rhs.size());
2120 result.push_back(lhs);
2125 template <typename E, class T, class A, class S>
2127 basic_fbstring<E, T, A, S> operator+(
2128 const basic_fbstring<E, T, A, S>& lhs,
2129 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2131 typedef typename basic_fbstring<E, T, A, S>::size_type size_type;
2132 typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;
2134 basic_fbstring<E, T, A, S> result;
2135 const size_type len = traits_type::length(rhs);
2136 result.reserve(lhs.size() + len);
2137 result.append(lhs).append(rhs, len);
2141 template <typename E, class T, class A, class S>
2143 basic_fbstring<E, T, A, S> operator+(
2144 const basic_fbstring<E, T, A, S>& lhs,
2145 typename basic_fbstring<E, T, A, S>::value_type rhs) {
2147 basic_fbstring<E, T, A, S> result;
2148 result.reserve(lhs.size() + 1);
2150 result.push_back(rhs);
2154 template <typename E, class T, class A, class S>
2156 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2157 const basic_fbstring<E, T, A, S>& rhs) {
2158 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; }
2160 template <typename E, class T, class A, class S>
2162 bool operator==(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2163 const basic_fbstring<E, T, A, S>& rhs) {
2164 return rhs == lhs; }
2166 template <typename E, class T, class A, class S>
2168 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2169 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2170 return lhs.compare(rhs) == 0; }
2172 template <typename E, class T, class A, class S>
2174 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2175 const basic_fbstring<E, T, A, S>& rhs) {
2176 return !(lhs == rhs); }
2178 template <typename E, class T, class A, class S>
2180 bool operator!=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2181 const basic_fbstring<E, T, A, S>& rhs) {
2182 return !(lhs == rhs); }
2184 template <typename E, class T, class A, class S>
2186 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2187 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2188 return !(lhs == rhs); }
2190 template <typename E, class T, class A, class S>
2192 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2193 const basic_fbstring<E, T, A, S>& rhs) {
2194 return lhs.compare(rhs) < 0; }
2196 template <typename E, class T, class A, class S>
2198 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2199 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2200 return lhs.compare(rhs) < 0; }
2202 template <typename E, class T, class A, class S>
2204 bool operator<(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2205 const basic_fbstring<E, T, A, S>& rhs) {
2206 return rhs.compare(lhs) > 0; }
2208 template <typename E, class T, class A, class S>
2210 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2211 const basic_fbstring<E, T, A, S>& rhs) {
2214 template <typename E, class T, class A, class S>
2216 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2217 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2220 template <typename E, class T, class A, class S>
2222 bool operator>(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2223 const basic_fbstring<E, T, A, S>& rhs) {
2226 template <typename E, class T, class A, class S>
2228 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2229 const basic_fbstring<E, T, A, S>& rhs) {
2230 return !(rhs < lhs); }
2232 template <typename E, class T, class A, class S>
2234 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2235 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2236 return !(rhs < lhs); }
2238 template <typename E, class T, class A, class S>
2240 bool operator<=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2241 const basic_fbstring<E, T, A, S>& rhs) {
2242 return !(rhs < lhs); }
2244 template <typename E, class T, class A, class S>
2246 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2247 const basic_fbstring<E, T, A, S>& rhs) {
2248 return !(lhs < rhs); }
2250 template <typename E, class T, class A, class S>
2252 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2253 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2254 return !(lhs < rhs); }
2256 template <typename E, class T, class A, class S>
2258 bool operator>=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2259 const basic_fbstring<E, T, A, S>& rhs) {
2260 return !(lhs < rhs);
2264 template <typename E, class T, class A, class S>
2265 void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {
2269 // TODO: make this faster.
2270 template <typename E, class T, class A, class S>
2273 typename basic_fbstring<E, T, A, S>::value_type,
2274 typename basic_fbstring<E, T, A, S>::traits_type>&
2276 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2277 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2278 basic_fbstring<E, T, A, S>& str) {
2279 typename std::basic_istream<E, T>::sentry sentry(is);
2280 typedef std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2281 typename basic_fbstring<E, T, A, S>::traits_type>
2283 typedef typename __istream_type::ios_base __ios_base;
2284 size_t extracted = 0;
2285 auto err = __ios_base::goodbit;
2287 auto n = is.width();
2292 auto got = is.rdbuf()->sgetc();
2293 for (; extracted != n && got != T::eof() && !isspace(got); ++extracted) {
2294 // Whew. We get to store this guy
2296 got = is.rdbuf()->snextc();
2298 if (got == T::eof()) {
2299 err |= __ios_base::eofbit;
2304 err |= __ios_base::failbit;
2312 template <typename E, class T, class A, class S>
2314 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2315 typename basic_fbstring<E, T, A, S>::traits_type>&
2317 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2318 typename basic_fbstring<E, T, A, S>::traits_type>& os,
2319 const basic_fbstring<E, T, A, S>& str) {
2321 typename std::basic_ostream<
2322 typename basic_fbstring<E, T, A, S>::value_type,
2323 typename basic_fbstring<E, T, A, S>::traits_type>::sentry __s(os);
2325 typedef std::ostreambuf_iterator<
2326 typename basic_fbstring<E, T, A, S>::value_type,
2327 typename basic_fbstring<E, T, A, S>::traits_type> _Ip;
2328 size_t __len = str.size();
2330 (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
2331 if (__pad_and_output(_Ip(os),
2333 __left ? str.data() + __len : str.data(),
2336 os.fill()).failed()) {
2337 os.setstate(std::ios_base::badbit | std::ios_base::failbit);
2341 std::__ostream_insert(os, str.data(), str.size());
2346 #ifndef _LIBSTDCXX_FBSTRING
2348 template <typename E, class T, class A, class S>
2350 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2351 typename basic_fbstring<E, T, A, S>::traits_type>&
2353 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2354 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2355 basic_fbstring<E, T, A, S>& str,
2356 typename basic_fbstring<E, T, A, S>::value_type delim) {
2357 // Use the nonstandard getdelim()
2361 // This looks quadratic but it really depends on realloc
2362 auto const newSize = size + 128;
2363 buf = static_cast<char*>(checkedRealloc(buf, newSize));
2364 is.getline(buf + size, newSize - size, delim);
2365 if (is.bad() || is.eof() || !is.fail()) {
2366 // done by either failure, end of file, or normal read
2367 size += std::strlen(buf + size);
2370 // Here we have failed due to too short a buffer
2371 // Minus one to discount the terminating '\0'
2373 assert(buf[size] == 0);
2374 // Clear the error so we can continue reading
2377 basic_fbstring<E, T, A, S> result(buf, size, size + 1,
2378 AcquireMallocatedString());
2383 template <typename E, class T, class A, class S>
2385 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2386 typename basic_fbstring<E, T, A, S>::traits_type>&
2388 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2389 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2390 basic_fbstring<E, T, A, S>& str) {
2391 // Just forward to the version with a delimiter
2392 return getline(is, str, '\n');
2397 template <typename E1, class T, class A, class S>
2398 const typename basic_fbstring<E1, T, A, S>::size_type
2399 basic_fbstring<E1, T, A, S>::npos =
2400 static_cast<typename basic_fbstring<E1, T, A, S>::size_type>(-1);
2402 #ifndef _LIBSTDCXX_FBSTRING
2403 // basic_string compatibility routines
2405 template <typename E, class T, class A, class S>
2407 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2408 const std::string& rhs) {
2409 return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;
2412 template <typename E, class T, class A, class S>
2414 bool operator==(const std::string& lhs,
2415 const basic_fbstring<E, T, A, S>& rhs) {
2419 template <typename E, class T, class A, class S>
2421 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2422 const std::string& rhs) {
2423 return !(lhs == rhs);
2426 template <typename E, class T, class A, class S>
2428 bool operator!=(const std::string& lhs,
2429 const basic_fbstring<E, T, A, S>& rhs) {
2430 return !(lhs == rhs);
2433 #if !defined(_LIBSTDCXX_FBSTRING)
2434 typedef basic_fbstring<char> fbstring;
2437 // fbstring is relocatable
2438 template <class T, class R, class A, class S>
2439 FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);
2442 _GLIBCXX_END_NAMESPACE_VERSION
2445 } // namespace folly
2447 #ifndef _LIBSTDCXX_FBSTRING
2449 // Hash functions to make fbstring usable with e.g. hash_map
2451 // Handle interaction with different C++ standard libraries, which
2452 // expect these types to be in different namespaces.
2456 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2457 size_t operator()(const folly::basic_fbstring<C> & s) const {
2458 return hash<const C*>::operator()(s.c_str());
2463 struct hash< ::folly::fbstring> {
2464 size_t operator()(const ::folly::fbstring& s) const {
2465 return ::folly::hash::fnv32_buf(s.data(), s.size());
2471 #if defined(_GLIBCXX_SYMVER) && !defined(__BIONIC__)
2472 namespace __gnu_cxx {
2475 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2476 size_t operator()(const folly::basic_fbstring<C> & s) const {
2477 return hash<const C*>::operator()(s.c_str());
2482 struct hash< ::folly::fbstring> {
2483 size_t operator()(const ::folly::fbstring& s) const {
2484 return ::folly::hash::fnv32_buf(s.data(), s.size());
2489 #endif // _GLIBCXX_SYMVER && !__BIONIC__
2490 #endif // _LIBSTDCXX_FBSTRING
2492 #pragma GCC diagnostic pop
2494 #undef FBSTRING_DISABLE_ADDRESS_SANITIZER
2496 #undef FBSTRING_LIKELY
2497 #undef FBSTRING_UNLIKELY
2499 #endif // FOLLY_BASE_FBSTRING_H_