2 * Copyright 2014 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // @author: Andrei Alexandrescu (aalexandre)
20 #ifndef FOLLY_BASE_FBSTRING_H_
21 #define FOLLY_BASE_FBSTRING_H_
24 fbstring's behavior can be configured via two macro definitions, as
25 follows. Normally, fbstring does not write a '\0' at the end of
26 each string whenever it changes the underlying characters. Instead,
27 it lazily writes the '\0' whenever either c_str() or data()
30 This is standard-compliant behavior and may save costs in some
31 circumstances. However, it may be surprising to some client code
32 because c_str() and data() are const member functions (fbstring
33 uses the "mutable" storage class for its own state).
35 In order to appease client code that expects fbstring to be
36 zero-terminated at all times, if the preprocessor symbol
37 FBSTRING_CONSERVATIVE is defined, fbstring does exactly that,
38 i.e. it goes the extra mile to guarantee a '\0' is always planted
39 at the end of its data.
41 On the contrary, if the desire is to debug faulty client code that
42 unduly assumes the '\0' is present, fbstring plants a '^' (i.e.,
43 emphatically NOT a zero) at the end of each string if
44 FBSTRING_PERVERSE is defined. (Calling c_str() or data() still
45 writes the '\0', of course.)
47 The preprocessor symbols FBSTRING_PERVERSE and
48 FBSTRING_CONSERVATIVE cannot be defined simultaneously. This is
49 enforced during preprocessing.
52 //#define FBSTRING_PERVERSE
53 //#define FBSTRING_CONSERVATIVE
55 #ifdef FBSTRING_PERVERSE
56 #ifdef FBSTRING_CONSERVATIVE
57 #error Cannot define both FBSTRING_PERVERSE and FBSTRING_CONSERVATIVE.
63 #include <type_traits>
65 // libc++ doesn't provide this header
66 #ifndef _LIBCPP_VERSION
67 // This file appears in two locations: inside fbcode and in the
68 // libstdc++ source code (when embedding fbstring as std::string).
69 // To aid in this schizophrenic use, two macros are defined in
71 // _LIBSTDCXX_FBSTRING - Set inside libstdc++. This is useful to
72 // gate use inside fbcode v. libstdc++
73 #include <bits/c++config.h>
76 #ifdef _LIBSTDCXX_FBSTRING
78 #pragma GCC system_header
80 // Handle the cases where the fbcode version (folly/Malloc.h) is included
81 // either before or after this inclusion.
82 #ifdef FOLLY_MALLOC_H_
83 #undef FOLLY_MALLOC_H_
84 #include "basic_fbstring_malloc.h"
86 #include "basic_fbstring_malloc.h"
87 #undef FOLLY_MALLOC_H_
90 #else // !_LIBSTDCXX_FBSTRING
96 #include "folly/Traits.h"
97 #include "folly/Malloc.h"
98 #include "folly/Hash.h"
100 #ifdef _GLIBCXX_SYMVER
101 #include <ext/hash_set>
102 #include <ext/hash_map>
107 // We defined these here rather than including Likely.h to avoid
108 // redefinition errors when fbstring is imported into libstdc++.
109 #define FBSTRING_LIKELY(x) (__builtin_expect((x), 1))
110 #define FBSTRING_UNLIKELY(x) (__builtin_expect((x), 0))
112 // Ignore shadowing warnings within this file, so includers can use -Wshadow.
113 #pragma GCC diagnostic push
114 #pragma GCC diagnostic ignored "-Wshadow"
116 // FBString cannot use throw when replacing std::string, though it may still
117 // use std::__throw_*
118 #define throw FOLLY_FBSTRING_MAY_NOT_USE_THROW
120 #ifdef _LIBSTDCXX_FBSTRING
121 namespace std _GLIBCXX_VISIBILITY(default) {
122 _GLIBCXX_BEGIN_NAMESPACE_VERSION
127 // Different versions of gcc/clang support different versions of
128 // the address sanitizer attribute. Unfortunately, this attribute
129 // has issues when inlining is used, so disable that as well.
130 #if defined(__clang__)
131 # if __has_feature(address_sanitizer)
132 # if __has_attribute(__no_address_safety_analysis__)
133 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
134 __attribute__((__no_address_safety_analysis__, __noinline__))
135 # elif __has_attribute(__no_sanitize_address__)
136 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
137 __attribute__((__no_sanitize_address__, __noinline__))
140 #elif defined (__GNUC__) && \
142 (__GNUC_MINOR__ >= 8) && \
144 # define FBSTRING_DISABLE_ADDRESS_SANITIZER \
145 __attribute__((__no_address_safety_analysis__, __noinline__))
147 #ifndef FBSTRING_DISABLE_ADDRESS_SANITIZER
148 # define FBSTRING_DISABLE_ADDRESS_SANITIZER
151 namespace fbstring_detail {
153 template <class InIt, class OutIt>
156 typename std::iterator_traits<InIt>::difference_type n,
158 for (; n != 0; --n, ++b, ++d) {
164 template <class Pod, class T>
165 inline void pod_fill(Pod* b, Pod* e, T c) {
166 assert(b && e && b <= e);
167 /*static*/ if (sizeof(T) == 1) {
170 auto const ee = b + ((e - b) & ~7u);
171 for (; b != ee; b += 8) {
182 for (; b != e; ++b) {
189 * Lightly structured memcpy, simplifies copying PODs and introduces
190 * some asserts. Unfortunately using this function may cause
191 * measurable overhead (presumably because it adjusts from a begin/end
192 * convention to a pointer/size convention, so it does some extra
193 * arithmetic even though the caller might have done the inverse
194 * adaptation outside).
197 inline void pod_copy(const Pod* b, const Pod* e, Pod* d) {
199 assert(d >= e || d + (e - b) <= b);
200 memcpy(d, b, (e - b) * sizeof(Pod));
204 * Lightly structured memmove, simplifies copying PODs and introduces
208 inline void pod_move(const Pod* b, const Pod* e, Pod* d) {
210 memmove(d, b, (e - b) * sizeof(*b));
213 } // namespace fbstring_detail
216 * Defines a special acquisition method for constructing fbstring
217 * objects. AcquireMallocatedString means that the user passes a
218 * pointer to a malloc-allocated string that the fbstring object will
221 enum class AcquireMallocatedString {};
224 * fbstring_core_model is a mock-up type that defines all required
225 * signatures of a fbstring core. The fbstring class itself uses such
226 * a core object to implement all of the numerous member functions
227 * required by the standard.
229 * If you want to define a new core, copy the definition below and
230 * implement the primitives. Then plug the core into basic_fbstring as
231 * a template argument.
233 template <class Char>
234 class fbstring_core_model {
236 fbstring_core_model();
237 fbstring_core_model(const fbstring_core_model &);
238 ~fbstring_core_model();
239 // Returns a pointer to string's buffer (currently only contiguous
240 // strings are supported). The pointer is guaranteed to be valid
241 // until the next call to a non-const member function.
242 const Char * data() const;
243 // Much like data(), except the string is prepared to support
244 // character-level changes. This call is a signal for
245 // e.g. reference-counted implementation to fork the data. The
246 // pointer is guaranteed to be valid until the next call to a
247 // non-const member function.
248 Char * mutable_data();
249 // Returns a pointer to string's buffer and guarantees that a
250 // readable '\0' lies right after the buffer. The pointer is
251 // guaranteed to be valid until the next call to a non-const member
253 const Char * c_str() const;
254 // Shrinks the string by delta characters. Asserts that delta <=
256 void shrink(size_t delta);
257 // Expands the string by delta characters (i.e. after this call
258 // size() will report the old size() plus delta) but without
259 // initializing the expanded region. Returns a pointer to the memory
260 // to be initialized (the beginning of the expanded portion). The
261 // caller is expected to fill the expanded area appropriately.
262 Char* expand_noinit(size_t delta);
263 // Expands the string by one character and sets the last character
265 void push_back(Char c);
266 // Returns the string's size.
268 // Returns the string's capacity, i.e. maximum size that the string
269 // can grow to without reallocation. Note that for reference counted
270 // strings that's technically a lie - even assigning characters
271 // within the existing size would cause a reallocation.
272 size_t capacity() const;
273 // Returns true if the data underlying the string is actually shared
274 // across multiple strings (in a refcounted fashion).
275 bool isShared() const;
276 // Makes sure that at least minCapacity characters are available for
277 // the string without reallocation. For reference-counted strings,
278 // it should fork the data even if minCapacity < size().
279 void reserve(size_t minCapacity);
282 fbstring_core_model& operator=(const fbstring_core_model &);
287 * gcc-4.7 throws what appears to be some false positive uninitialized
288 * warnings for the members of the MediumLarge struct. So, mute them here.
290 #if defined(__GNUC__) && !defined(__clang__)
291 # pragma GCC diagnostic push
292 # pragma GCC diagnostic ignored "-Wuninitialized"
296 * This is the core of the string. The code should work on 32- and
297 * 64-bit architectures and with any Char size. Porting to big endian
298 * architectures would require some changes.
300 * The storage is selected as follows (assuming we store one-byte
301 * characters on a 64-bit machine): (a) "small" strings between 0 and
302 * 23 chars are stored in-situ without allocation (the rightmost byte
303 * stores the size); (b) "medium" strings from 24 through 254 chars
304 * are stored in malloc-allocated memory that is copied eagerly; (c)
305 * "large" strings of 255 chars and above are stored in a similar
306 * structure as medium arrays, except that the string is
307 * reference-counted and copied lazily. the reference count is
308 * allocated right before the character array.
310 * The discriminator between these three strategies sits in the two
311 * most significant bits of the rightmost char of the storage. If
312 * neither is set, then the string is small (and its length sits in
313 * the lower-order bits of that rightmost character). If the MSb is
314 * set, the string is medium width. If the second MSb is set, then the
317 template <class Char> class fbstring_core {
319 fbstring_core() noexcept {
320 // Only initialize the tag, will set the MSBs (i.e. the small
321 // string size) to zero too
322 ml_.capacity_ = maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char)));
323 // or: setSmallSize(0);
325 assert(category() == isSmall && size() == 0);
328 fbstring_core(const fbstring_core & rhs) {
329 assert(&rhs != this);
330 // Simplest case first: small strings are bitblitted
331 if (rhs.category() == isSmall) {
332 assert(offsetof(MediumLarge, data_) == 0);
333 assert(offsetof(MediumLarge, size_) == sizeof(ml_.data_));
334 assert(offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_));
335 const size_t size = rhs.smallSize();
337 ml_.capacity_ = rhs.ml_.capacity_;
340 // Just write the whole thing, don't look at details. In
341 // particular we need to copy capacity anyway because we want
342 // to set the size (don't forget that the last character,
343 // which stores a short string's length, is shared with the
344 // ml_.capacity field).
347 assert(category() == isSmall && this->size() == rhs.size());
348 } else if (rhs.category() == isLarge) {
349 // Large strings are just refcounted
351 RefCounted::incrementRefs(ml_.data_);
352 assert(category() == isLarge && size() == rhs.size());
354 // Medium strings are copied eagerly. Don't forget to allocate
355 // one extra Char for the null terminator.
356 auto const allocSize =
357 goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));
358 ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
359 fbstring_detail::pod_copy(rhs.ml_.data_,
361 rhs.ml_.data_ + rhs.ml_.size_ + 1,
363 // No need for writeTerminator() here, we copied one extra
364 // element just above.
365 ml_.size_ = rhs.ml_.size_;
366 ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
367 assert(category() == isMedium);
369 assert(size() == rhs.size());
370 assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);
373 fbstring_core(fbstring_core&& goner) noexcept {
374 if (goner.category() == isSmall) {
375 // Just copy, leave the goner in peace
376 new(this) fbstring_core(goner.small_, goner.smallSize());
380 // Clean goner's carcass
381 goner.setSmallSize(0);
385 // NOTE(agallagher): The word-aligned copy path copies bytes which are
386 // outside the range of the string, and makes address sanitizer unhappy,
387 // so just disable it on this function.
388 fbstring_core(const Char *const data, const size_t size)
389 FBSTRING_DISABLE_ADDRESS_SANITIZER {
390 // Simplest case first: small strings are bitblitted
391 if (size <= maxSmallSize) {
392 // Layout is: Char* data_, size_t size_, size_t capacity_
393 /*static_*/assert(sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t));
394 /*static_*/assert(sizeof(Char*) == sizeof(size_t));
395 // sizeof(size_t) must be a power of 2
396 /*static_*/assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
398 // If data is aligned, use fast word-wise copying. Otherwise,
399 // use conservative memcpy.
400 if (reinterpret_cast<size_t>(data) & (sizeof(size_t) - 1)) {
401 fbstring_detail::pod_copy(data, data + size, small_);
403 // Copy one word (64 bits) at a time
404 const size_t byteSize = size * sizeof(Char);
405 if (byteSize > 2 * sizeof(size_t)) {
407 ml_.capacity_ = reinterpret_cast<const size_t*>(data)[2];
409 ml_.size_ = reinterpret_cast<const size_t*>(data)[1];
411 ml_.data_ = *reinterpret_cast<Char**>(const_cast<Char*>(data));
412 } else if (byteSize > sizeof(size_t)) {
415 } else if (size > 0) {
421 } else if (size <= maxMediumSize) {
422 // Medium strings are allocated normally. Don't forget to
423 // allocate one extra Char for the terminating null.
424 auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));
425 ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
426 fbstring_detail::pod_copy(data, data + size, ml_.data_);
428 ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
430 // Large strings are allocated differently
431 size_t effectiveCapacity = size;
432 auto const newRC = RefCounted::create(data, & effectiveCapacity);
433 ml_.data_ = newRC->data_;
435 ml_.capacity_ = effectiveCapacity | isLarge;
438 assert(this->size() == size);
439 assert(memcmp(this->data(), data, size * sizeof(Char)) == 0);
442 ~fbstring_core() noexcept {
443 auto const c = category();
451 RefCounted::decrementRefs(ml_.data_);
454 // Snatches a previously mallocated string. The parameter "size"
455 // is the size of the string, and the parameter "allocatedSize"
456 // is the size of the mallocated block. The string must be
457 // \0-terminated, so allocatedSize >= size + 1 and data[size] == '\0'.
459 // So if you want a 2-character string, pass malloc(3) as "data",
460 // pass 2 as "size", and pass 3 as "allocatedSize".
461 fbstring_core(Char * const data,
463 const size_t allocatedSize,
464 AcquireMallocatedString) {
466 assert(allocatedSize >= size + 1);
467 assert(data[size] == '\0');
468 // Use the medium string storage
471 // Don't forget about null terminator
472 ml_.capacity_ = (allocatedSize - 1) | isMedium;
474 // No need for the memory
480 // swap below doesn't test whether &rhs == this (and instead
481 // potentially does extra work) on the premise that the rarity of
482 // that situation actually makes the check more expensive than is
484 void swap(fbstring_core & rhs) {
490 // In C++11 data() and c_str() are 100% equivalent.
491 const Char * data() const {
495 Char * mutable_data() {
496 auto const c = category();
500 assert(c == isMedium || c == isLarge);
501 if (c == isLarge && RefCounted::refs(ml_.data_) > 1) {
503 size_t effectiveCapacity = ml_.capacity();
504 auto const newRC = RefCounted::create(& effectiveCapacity);
505 // If this fails, someone placed the wrong capacity in an
507 assert(effectiveCapacity >= ml_.capacity());
508 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
510 RefCounted::decrementRefs(ml_.data_);
511 ml_.data_ = newRC->data_;
512 // No need to call writeTerminator(), we have + 1 above.
517 const Char * c_str() const {
518 auto const c = category();
519 #ifdef FBSTRING_PERVERSE
521 assert(small_[smallSize()] == TERMINATOR || smallSize() == maxSmallSize
522 || small_[smallSize()] == '\0');
523 small_[smallSize()] = '\0';
526 assert(c == isMedium || c == isLarge);
527 assert(ml_.data_[ml_.size_] == TERMINATOR || ml_.data_[ml_.size_] == '\0');
528 ml_.data_[ml_.size_] = '\0';
529 #elif defined(FBSTRING_CONSERVATIVE)
531 assert(small_[smallSize()] == '\0');
534 assert(c == isMedium || c == isLarge);
535 assert(ml_.data_[ml_.size_] == '\0');
538 small_[smallSize()] = '\0';
541 assert(c == isMedium || c == isLarge);
542 ml_.data_[ml_.size_] = '\0';
547 void shrink(const size_t delta) {
548 if (category() == isSmall) {
549 // Check for underflow
550 assert(delta <= smallSize());
551 setSmallSize(smallSize() - delta);
552 } else if (category() == isMedium || RefCounted::refs(ml_.data_) == 1) {
553 // Medium strings and unique large strings need no special
555 assert(ml_.size_ >= delta);
558 assert(ml_.size_ >= delta);
559 // Shared large string, must make unique. This is because of the
560 // durn terminator must be written, which may trample the shared
563 fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this);
565 // No need to write the terminator.
571 void reserve(size_t minCapacity) {
572 if (category() == isLarge) {
574 if (RefCounted::refs(ml_.data_) > 1) {
575 // We must make it unique regardless; in-place reallocation is
576 // useless if the string is shared. In order to not surprise
577 // people, reserve the new block at current capacity or
578 // more. That way, a string's capacity never shrinks after a
580 minCapacity = std::max(minCapacity, ml_.capacity());
581 auto const newRC = RefCounted::create(& minCapacity);
582 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1,
584 // Done with the old data. No need to call writeTerminator(),
585 // we have + 1 above.
586 RefCounted::decrementRefs(ml_.data_);
587 ml_.data_ = newRC->data_;
588 ml_.capacity_ = minCapacity | isLarge;
589 // size remains unchanged
591 // String is not shared, so let's try to realloc (if needed)
592 if (minCapacity > ml_.capacity()) {
593 // Asking for more memory
595 RefCounted::reallocate(ml_.data_, ml_.size_,
596 ml_.capacity(), minCapacity);
597 ml_.data_ = newRC->data_;
598 ml_.capacity_ = minCapacity | isLarge;
601 assert(capacity() >= minCapacity);
603 } else if (category() == isMedium) {
604 // String is not shared
605 if (minCapacity <= ml_.capacity()) {
606 return; // nothing to do, there's enough room
608 if (minCapacity <= maxMediumSize) {
609 // Keep the string at medium size. Don't forget to allocate
610 // one extra Char for the terminating null.
611 size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char));
612 ml_.data_ = static_cast<Char *>(
615 ml_.size_ * sizeof(Char),
616 (ml_.capacity() + 1) * sizeof(Char),
619 ml_.capacity_ = (capacityBytes / sizeof(Char) - 1) | isMedium;
621 // Conversion from medium to large string
622 fbstring_core nascent;
623 // Will recurse to another branch of this function
624 nascent.reserve(minCapacity);
625 nascent.ml_.size_ = ml_.size_;
626 fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_,
630 assert(capacity() >= minCapacity);
633 assert(category() == isSmall);
634 if (minCapacity > maxMediumSize) {
636 auto const newRC = RefCounted::create(& minCapacity);
637 auto const size = smallSize();
638 fbstring_detail::pod_copy(small_, small_ + size + 1, newRC->data_);
639 // No need for writeTerminator(), we wrote it above with + 1.
640 ml_.data_ = newRC->data_;
642 ml_.capacity_ = minCapacity | isLarge;
643 assert(capacity() >= minCapacity);
644 } else if (minCapacity > maxSmallSize) {
646 // Don't forget to allocate one extra Char for the terminating null
647 auto const allocSizeBytes =
648 goodMallocSize((1 + minCapacity) * sizeof(Char));
649 auto const data = static_cast<Char*>(checkedMalloc(allocSizeBytes));
650 auto const size = smallSize();
651 fbstring_detail::pod_copy(small_, small_ + size + 1, data);
652 // No need for writeTerminator(), we wrote it above with + 1.
655 ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1) | isMedium;
658 // Nothing to do, everything stays put
661 assert(capacity() >= minCapacity);
664 Char * expand_noinit(const size_t delta) {
665 // Strategy is simple: make room, then change size
666 assert(capacity() >= size());
668 if (category() == isSmall) {
671 if (newSz <= maxSmallSize) {
679 newSz = ml_.size_ + delta;
680 if (newSz > capacity()) {
684 assert(capacity() >= newSz);
685 // Category can't be small - we took care of that above
686 assert(category() == isMedium || category() == isLarge);
689 assert(size() == newSz);
690 return ml_.data_ + sz;
693 void push_back(Char c) {
694 assert(capacity() >= size());
696 if (category() == isSmall) {
698 if (sz < maxSmallSize) {
699 setSmallSize(sz + 1);
704 reserve(maxSmallSize * 2);
707 if (sz == capacity()) { // always true for isShared()
708 reserve(1 + sz * 3 / 2); // ensures not shared
712 assert(capacity() >= sz + 1);
713 // Category can't be small - we took care of that above
714 assert(category() == isMedium || category() == isLarge);
720 size_t size() const {
721 return category() == isSmall ? smallSize() : ml_.size_;
724 size_t capacity() const {
725 switch (category()) {
729 // For large-sized strings, a multi-referenced chunk has no
730 // available capacity. This is because any attempt to append
731 // data would trigger a new allocation.
732 if (RefCounted::refs(ml_.data_) > 1) return ml_.size_;
735 return ml_.capacity();
738 bool isShared() const {
739 return category() == isLarge && RefCounted::refs(ml_.data_) > 1;
742 #ifdef FBSTRING_PERVERSE
743 enum { TERMINATOR = '^' };
745 enum { TERMINATOR = '\0' };
748 void writeTerminator() {
749 #if defined(FBSTRING_PERVERSE) || defined(FBSTRING_CONSERVATIVE)
750 if (category() == isSmall) {
751 const auto s = smallSize();
752 if (s != maxSmallSize) {
753 small_[s] = TERMINATOR;
756 ml_.data_[ml_.size_] = TERMINATOR;
763 fbstring_core & operator=(const fbstring_core & rhs);
770 size_t capacity() const {
771 return capacity_ & capacityExtractMask;
776 std::atomic<size_t> refCount_;
779 static RefCounted * fromData(Char * p) {
780 return static_cast<RefCounted*>(
782 static_cast<unsigned char*>(static_cast<void*>(p))
783 - sizeof(refCount_)));
786 static size_t refs(Char * p) {
787 return fromData(p)->refCount_.load(std::memory_order_acquire);
790 static void incrementRefs(Char * p) {
791 fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel);
794 static void decrementRefs(Char * p) {
795 auto const dis = fromData(p);
796 size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel);
803 static RefCounted * create(size_t * size) {
804 // Don't forget to allocate one extra Char for the terminating
805 // null. In this case, however, one Char is already part of the
807 const size_t allocSize = goodMallocSize(
808 sizeof(RefCounted) + *size * sizeof(Char));
809 auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));
810 result->refCount_.store(1, std::memory_order_release);
811 *size = (allocSize - sizeof(RefCounted)) / sizeof(Char);
815 static RefCounted * create(const Char * data, size_t * size) {
816 const size_t effectiveSize = *size;
817 auto result = create(size);
818 fbstring_detail::pod_copy(data, data + effectiveSize, result->data_);
822 static RefCounted * reallocate(Char *const data,
823 const size_t currentSize,
824 const size_t currentCapacity,
825 const size_t newCapacity) {
826 assert(newCapacity > 0 && newCapacity > currentSize);
827 auto const dis = fromData(data);
828 assert(dis->refCount_.load(std::memory_order_acquire) == 1);
829 // Don't forget to allocate one extra Char for the terminating
830 // null. In this case, however, one Char is already part of the
832 auto result = static_cast<RefCounted*>(
834 sizeof(RefCounted) + currentSize * sizeof(Char),
835 sizeof(RefCounted) + currentCapacity * sizeof(Char),
836 sizeof(RefCounted) + newCapacity * sizeof(Char)));
837 assert(result->refCount_.load(std::memory_order_acquire) == 1);
843 mutable Char small_[sizeof(MediumLarge) / sizeof(Char)];
844 mutable MediumLarge ml_;
848 lastChar = sizeof(MediumLarge) - 1,
849 maxSmallSize = lastChar / sizeof(Char),
850 maxMediumSize = 254 / sizeof(Char), // coincides with the small
851 // bin size in dlmalloc
852 categoryExtractMask = sizeof(size_t) == 4 ? 0xC0000000 : 0xC000000000000000,
853 capacityExtractMask = ~categoryExtractMask,
855 static_assert(!(sizeof(MediumLarge) % sizeof(Char)),
856 "Corrupt memory layout for fbstring.");
860 isMedium = sizeof(size_t) == 4 ? 0x80000000 : 0x8000000000000000,
861 isLarge = sizeof(size_t) == 4 ? 0x40000000 : 0x4000000000000000,
864 Category category() const {
865 // Assumes little endian
866 return static_cast<Category>(ml_.capacity_ & categoryExtractMask);
869 size_t smallSize() const {
870 assert(category() == isSmall &&
871 static_cast<size_t>(small_[maxSmallSize])
872 <= static_cast<size_t>(maxSmallSize));
873 return static_cast<size_t>(maxSmallSize)
874 - static_cast<size_t>(small_[maxSmallSize]);
877 void setSmallSize(size_t s) {
878 // Warning: this should work with uninitialized strings too,
879 // so don't assume anything about the previous value of
880 // small_[maxSmallSize].
881 assert(s <= maxSmallSize);
882 small_[maxSmallSize] = maxSmallSize - s;
886 #if defined(__GNUC__) && !defined(__clang__)
887 # pragma GCC diagnostic pop
890 #ifndef _LIBSTDCXX_FBSTRING
892 * Dummy fbstring core that uses an actual std::string. This doesn't
893 * make any sense - it's just for testing purposes.
895 template <class Char>
896 class dummy_fbstring_core {
898 dummy_fbstring_core() {
900 dummy_fbstring_core(const dummy_fbstring_core& another)
901 : backend_(another.backend_) {
903 dummy_fbstring_core(const Char * s, size_t n)
906 void swap(dummy_fbstring_core & rhs) {
907 backend_.swap(rhs.backend_);
909 const Char * data() const {
910 return backend_.data();
912 Char * mutable_data() {
913 //assert(!backend_.empty());
914 return &*backend_.begin();
916 void shrink(size_t delta) {
917 assert(delta <= size());
918 backend_.resize(size() - delta);
920 Char * expand_noinit(size_t delta) {
921 auto const sz = size();
922 backend_.resize(size() + delta);
923 return backend_.data() + sz;
925 void push_back(Char c) {
926 backend_.push_back(c);
928 size_t size() const {
929 return backend_.size();
931 size_t capacity() const {
932 return backend_.capacity();
934 bool isShared() const {
937 void reserve(size_t minCapacity) {
938 backend_.reserve(minCapacity);
942 std::basic_string<Char> backend_;
944 #endif // !_LIBSTDCXX_FBSTRING
947 * This is the basic_string replacement. For conformity,
948 * basic_fbstring takes the same template parameters, plus the last
949 * one which is the core.
951 #ifdef _LIBSTDCXX_FBSTRING
952 template <typename E, class T, class A, class Storage>
954 template <typename E,
955 class T = std::char_traits<E>,
956 class A = std::allocator<E>,
957 class Storage = fbstring_core<E> >
959 class basic_fbstring {
963 void (*throw_exc)(const char*),
965 if (!condition) throw_exc(msg);
968 bool isSane() const {
971 empty() == (size() == 0) &&
972 empty() == (begin() == end()) &&
973 size() <= max_size() &&
974 capacity() <= max_size() &&
975 size() <= capacity() &&
976 (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0');
980 friend struct Invariant;
983 explicit Invariant(const basic_fbstring& s) : s_(s) {
990 const basic_fbstring& s_;
992 explicit Invariant(const basic_fbstring&) {}
994 Invariant& operator=(const Invariant&);
999 typedef T traits_type;
1000 typedef typename traits_type::char_type value_type;
1001 typedef A allocator_type;
1002 typedef typename A::size_type size_type;
1003 typedef typename A::difference_type difference_type;
1005 typedef typename A::reference reference;
1006 typedef typename A::const_reference const_reference;
1007 typedef typename A::pointer pointer;
1008 typedef typename A::const_pointer const_pointer;
1010 typedef E* iterator;
1011 typedef const E* const_iterator;
1012 typedef std::reverse_iterator<iterator
1013 #ifdef NO_ITERATOR_TRAITS
1017 typedef std::reverse_iterator<const_iterator
1018 #ifdef NO_ITERATOR_TRAITS
1021 > const_reverse_iterator;
1023 static const size_type npos; // = size_type(-1)
1026 static void procrustes(size_type& n, size_type nmax) {
1027 if (n > nmax) n = nmax;
1031 // C++11 21.4.2 construct/copy/destroy
1032 explicit basic_fbstring(const A& a = A()) noexcept {
1035 basic_fbstring(const basic_fbstring& str)
1036 : store_(str.store_) {
1040 basic_fbstring(basic_fbstring&& goner) noexcept
1041 : store_(std::move(goner.store_)) {
1044 #ifndef _LIBSTDCXX_FBSTRING
1045 // This is defined for compatibility with std::string
1046 /* implicit */ basic_fbstring(const std::string& str)
1047 : store_(str.data(), str.size()) {
1051 basic_fbstring(const basic_fbstring& str, size_type pos,
1052 size_type n = npos, const A& a = A()) {
1053 assign(str, pos, n);
1056 /* implicit */ basic_fbstring(const value_type* s, const A& a = A())
1057 : store_(s, s ? traits_type::length(s) : ({
1058 basic_fbstring<char> err = __PRETTY_FUNCTION__;
1059 err += ": null pointer initializer not valid";
1060 std::__throw_logic_error(err.c_str());
1065 basic_fbstring(const value_type* s, size_type n, const A& a = A())
1069 basic_fbstring(size_type n, value_type c, const A& a = A()) {
1070 auto const data = store_.expand_noinit(n);
1071 fbstring_detail::pod_fill(data, data + n, c);
1072 store_.writeTerminator();
1075 template <class InIt>
1076 basic_fbstring(InIt begin, InIt end,
1077 typename std::enable_if<
1078 !std::is_same<typename std::remove_const<InIt>::type,
1079 value_type*>::value, const A>::type & a = A()) {
1083 // Specialization for const char*, const char*
1084 basic_fbstring(const value_type* b, const value_type* e)
1085 : store_(b, e - b) {
1088 // Nonstandard constructor
1089 basic_fbstring(value_type *s, size_type n, size_type c,
1090 AcquireMallocatedString a)
1091 : store_(s, n, c, a) {
1094 // Construction from initialization list
1095 basic_fbstring(std::initializer_list<value_type> il) {
1096 assign(il.begin(), il.end());
1099 ~basic_fbstring() noexcept {
1102 basic_fbstring& operator=(const basic_fbstring& lhs) {
1103 if (FBSTRING_UNLIKELY(&lhs == this)) {
1106 auto const oldSize = size();
1107 auto const srcSize = lhs.size();
1108 if (capacity() >= srcSize && !store_.isShared()) {
1109 // great, just copy the contents
1110 if (oldSize < srcSize)
1111 store_.expand_noinit(srcSize - oldSize);
1113 store_.shrink(oldSize - srcSize);
1114 assert(size() == srcSize);
1115 fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin());
1116 store_.writeTerminator();
1118 // need to reallocate, so we may as well create a brand new string
1119 basic_fbstring(lhs).swap(*this);
1125 basic_fbstring& operator=(basic_fbstring&& goner) noexcept {
1126 if (FBSTRING_UNLIKELY(&goner == this)) {
1127 // Compatibility with std::basic_string<>,
1128 // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.
1131 // No need of this anymore
1132 this->~basic_fbstring();
1133 // Move the goner into this
1134 new(&store_) fbstring_core<E>(std::move(goner.store_));
1138 #ifndef _LIBSTDCXX_FBSTRING
1139 // Compatibility with std::string
1140 basic_fbstring & operator=(const std::string & rhs) {
1141 return assign(rhs.data(), rhs.size());
1144 // Compatibility with std::string
1145 std::string toStdString() const {
1146 return std::string(data(), size());
1149 // A lot of code in fbcode still uses this method, so keep it here for now.
1150 const basic_fbstring& toStdString() const {
1155 basic_fbstring& operator=(const value_type* s) {
1159 basic_fbstring& operator=(value_type c) {
1161 store_.expand_noinit(1);
1162 } else if (store_.isShared()) {
1163 basic_fbstring(1, c).swap(*this);
1166 store_.shrink(size() - 1);
1168 *store_.mutable_data() = c;
1169 store_.writeTerminator();
1173 basic_fbstring& operator=(std::initializer_list<value_type> il) {
1174 return assign(il.begin(), il.end());
1177 // C++11 21.4.3 iterators:
1178 iterator begin() { return store_.mutable_data(); }
1180 const_iterator begin() const { return store_.data(); }
1182 const_iterator cbegin() const { return begin(); }
1185 return store_.mutable_data() + store_.size();
1188 const_iterator end() const {
1189 return store_.data() + store_.size();
1192 const_iterator cend() const { return end(); }
1194 reverse_iterator rbegin() {
1195 return reverse_iterator(end());
1198 const_reverse_iterator rbegin() const {
1199 return const_reverse_iterator(end());
1202 const_reverse_iterator crbegin() const { return rbegin(); }
1204 reverse_iterator rend() {
1205 return reverse_iterator(begin());
1208 const_reverse_iterator rend() const {
1209 return const_reverse_iterator(begin());
1212 const_reverse_iterator crend() const { return rend(); }
1215 // C++11 21.4.5, element access:
1216 const value_type& front() const { return *begin(); }
1217 const value_type& back() const {
1219 // Should be begin()[size() - 1], but that branches twice
1220 return *(end() - 1);
1222 value_type& front() { return *begin(); }
1223 value_type& back() {
1225 // Should be begin()[size() - 1], but that branches twice
1226 return *(end() - 1);
1233 // C++11 21.4.4 capacity:
1234 size_type size() const { return store_.size(); }
1236 size_type length() const { return size(); }
1238 size_type max_size() const {
1239 return std::numeric_limits<size_type>::max();
1242 void resize(const size_type n, const value_type c = value_type()) {
1243 auto size = this->size();
1245 store_.shrink(size - n);
1247 // Do this in two steps to minimize slack memory copied (see
1249 auto const capacity = this->capacity();
1250 assert(capacity >= size);
1251 if (size < capacity) {
1252 auto delta = std::min(n, capacity) - size;
1253 store_.expand_noinit(delta);
1254 fbstring_detail::pod_fill(begin() + size, end(), c);
1257 store_.writeTerminator();
1262 auto const delta = n - size;
1263 store_.expand_noinit(delta);
1264 fbstring_detail::pod_fill(end() - delta, end(), c);
1265 store_.writeTerminator();
1267 assert(this->size() == n);
1270 size_type capacity() const { return store_.capacity(); }
1272 void reserve(size_type res_arg = 0) {
1273 enforce(res_arg <= max_size(), std::__throw_length_error, "");
1274 store_.reserve(res_arg);
1277 void shrink_to_fit() {
1278 // Shrink only if slack memory is sufficiently large
1279 if (capacity() < size() * 3 / 2) {
1282 basic_fbstring(cbegin(), cend()).swap(*this);
1285 void clear() { resize(0); }
1287 bool empty() const { return size() == 0; }
1289 // C++11 21.4.5 element access:
1290 const_reference operator[](size_type pos) const {
1291 return *(c_str() + pos);
1294 reference operator[](size_type pos) {
1295 if (pos == size()) {
1296 // Just call c_str() to make sure '\0' is present
1299 return *(begin() + pos);
1302 const_reference at(size_type n) const {
1303 enforce(n <= size(), std::__throw_out_of_range, "");
1307 reference at(size_type n) {
1308 enforce(n < size(), std::__throw_out_of_range, "");
1312 // C++11 21.4.6 modifiers:
1313 basic_fbstring& operator+=(const basic_fbstring& str) {
1317 basic_fbstring& operator+=(const value_type* s) {
1321 basic_fbstring& operator+=(const value_type c) {
1326 basic_fbstring& operator+=(std::initializer_list<value_type> il) {
1331 basic_fbstring& append(const basic_fbstring& str) {
1333 auto desiredSize = size() + str.size();
1335 append(str.data(), str.size());
1336 assert(size() == desiredSize);
1340 basic_fbstring& append(const basic_fbstring& str, const size_type pos,
1342 const size_type sz = str.size();
1343 enforce(pos <= sz, std::__throw_out_of_range, "");
1344 procrustes(n, sz - pos);
1345 return append(str.data() + pos, n);
1348 basic_fbstring& append(const value_type* s, size_type n) {
1350 Invariant checker(*this);
1353 if (FBSTRING_UNLIKELY(!n)) {
1354 // Unlikely but must be done
1357 auto const oldSize = size();
1358 auto const oldData = data();
1359 // Check for aliasing (rare). We could use "<=" here but in theory
1360 // those do not work for pointers unless the pointers point to
1361 // elements in the same array. For that reason we use
1362 // std::less_equal, which is guaranteed to offer a total order
1363 // over pointers. See discussion at http://goo.gl/Cy2ya for more
1365 std::less_equal<const value_type*> le;
1366 if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {
1367 assert(le(s + n, oldData + oldSize));
1368 const size_type offset = s - oldData;
1369 store_.reserve(oldSize + n);
1370 // Restore the source
1371 s = data() + offset;
1373 // Warning! Repeated appends with short strings may actually incur
1374 // practically quadratic performance. Avoid that by pushing back
1375 // the first character (which ensures exponential growth) and then
1376 // appending the rest normally. Worst case the append may incur a
1377 // second allocation but that will be rare.
1380 memcpy(store_.expand_noinit(n), s, n * sizeof(value_type));
1381 assert(size() == oldSize + n + 1);
1385 basic_fbstring& append(const value_type* s) {
1386 return append(s, traits_type::length(s));
1389 basic_fbstring& append(size_type n, value_type c) {
1390 resize(size() + n, c);
1394 template<class InputIterator>
1395 basic_fbstring& append(InputIterator first, InputIterator last) {
1396 insert(end(), first, last);
1400 basic_fbstring& append(std::initializer_list<value_type> il) {
1401 return append(il.begin(), il.end());
1404 void push_back(const value_type c) { // primitive
1405 store_.push_back(c);
1408 basic_fbstring& assign(const basic_fbstring& str) {
1409 if (&str == this) return *this;
1410 return assign(str.data(), str.size());
1413 basic_fbstring& assign(basic_fbstring&& str) {
1414 return *this = std::move(str);
1417 basic_fbstring& assign(const basic_fbstring& str, const size_type pos,
1419 const size_type sz = str.size();
1420 enforce(pos <= sz, std::__throw_out_of_range, "");
1421 procrustes(n, sz - pos);
1422 return assign(str.data() + pos, n);
1425 basic_fbstring& assign(const value_type* s, const size_type n) {
1426 Invariant checker(*this);
1429 std::copy(s, s + n, begin());
1431 assert(size() == n);
1433 const value_type *const s2 = s + size();
1434 std::copy(s, s2, begin());
1435 append(s2, n - size());
1436 assert(size() == n);
1438 store_.writeTerminator();
1439 assert(size() == n);
1443 basic_fbstring& assign(const value_type* s) {
1444 return assign(s, traits_type::length(s));
1447 basic_fbstring& assign(std::initializer_list<value_type> il) {
1448 return assign(il.begin(), il.end());
1451 template <class ItOrLength, class ItOrChar>
1452 basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {
1453 return replace(begin(), end(), first_or_n, last_or_c);
1456 basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {
1457 return insert(pos1, str.data(), str.size());
1460 basic_fbstring& insert(size_type pos1, const basic_fbstring& str,
1461 size_type pos2, size_type n) {
1462 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1463 procrustes(n, str.length() - pos2);
1464 return insert(pos1, str.data() + pos2, n);
1467 basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {
1468 enforce(pos <= length(), std::__throw_out_of_range, "");
1469 insert(begin() + pos, s, s + n);
1473 basic_fbstring& insert(size_type pos, const value_type* s) {
1474 return insert(pos, s, traits_type::length(s));
1477 basic_fbstring& insert(size_type pos, size_type n, value_type c) {
1478 enforce(pos <= length(), std::__throw_out_of_range, "");
1479 insert(begin() + pos, n, c);
1483 iterator insert(const_iterator p, const value_type c) {
1484 const size_type pos = p - begin();
1486 return begin() + pos;
1490 template <int i> class Selector {};
1492 iterator insertImplDiscr(const_iterator p,
1493 size_type n, value_type c, Selector<1>) {
1494 Invariant checker(*this);
1496 auto const pos = p - begin();
1497 assert(p >= begin() && p <= end());
1498 if (capacity() - size() < n) {
1499 const size_type sz = p - begin();
1500 reserve(size() + n);
1503 const iterator oldEnd = end();
1504 if (n < size_type(oldEnd - p)) {
1505 append(oldEnd - n, oldEnd);
1507 // reverse_iterator(oldEnd - n),
1508 // reverse_iterator(p),
1509 // reverse_iterator(oldEnd));
1510 fbstring_detail::pod_move(&*p, &*oldEnd - n,
1512 std::fill(begin() + pos, begin() + pos + n, c);
1514 append(n - (end() - p), c);
1515 append(iterator(p), oldEnd);
1516 std::fill(iterator(p), oldEnd, c);
1518 store_.writeTerminator();
1519 return begin() + pos;
1522 template<class InputIter>
1523 iterator insertImplDiscr(const_iterator i,
1524 InputIter b, InputIter e, Selector<0>) {
1525 return insertImpl(i, b, e,
1526 typename std::iterator_traits<InputIter>::iterator_category());
1529 template <class FwdIterator>
1530 iterator insertImpl(const_iterator i,
1531 FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) {
1532 Invariant checker(*this);
1534 const size_type pos = i - begin();
1535 const typename std::iterator_traits<FwdIterator>::difference_type n2 =
1536 std::distance(s1, s2);
1538 using namespace fbstring_detail;
1539 assert(pos <= size());
1541 const typename std::iterator_traits<FwdIterator>::difference_type maxn2 =
1542 capacity() - size();
1544 // realloc the string
1545 reserve(size() + n2);
1548 if (pos + n2 <= size()) {
1549 const iterator tailBegin = end() - n2;
1550 store_.expand_noinit(n2);
1551 fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2);
1552 std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i),
1553 reverse_iterator(tailBegin + n2));
1554 std::copy(s1, s2, begin() + pos);
1557 const size_type old_size = size();
1558 std::advance(t, old_size - pos);
1559 const size_t newElems = std::distance(t, s2);
1560 store_.expand_noinit(n2);
1561 std::copy(t, s2, begin() + old_size);
1562 fbstring_detail::pod_copy(data() + pos, data() + old_size,
1563 begin() + old_size + newElems);
1564 std::copy(s1, t, begin() + pos);
1566 store_.writeTerminator();
1567 return begin() + pos;
1570 template <class InputIterator>
1571 iterator insertImpl(const_iterator i,
1572 InputIterator b, InputIterator e,
1573 std::input_iterator_tag) {
1574 const auto pos = i - begin();
1575 basic_fbstring temp(begin(), i);
1576 for (; b != e; ++b) {
1579 temp.append(i, cend());
1581 return begin() + pos;
1585 template <class ItOrLength, class ItOrChar>
1586 iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {
1587 Selector<std::numeric_limits<ItOrLength>::is_specialized> sel;
1588 return insertImplDiscr(p, first_or_n, last_or_c, sel);
1591 iterator insert(const_iterator p, std::initializer_list<value_type> il) {
1592 return insert(p, il.begin(), il.end());
1595 basic_fbstring& erase(size_type pos = 0, size_type n = npos) {
1596 Invariant checker(*this);
1598 enforce(pos <= length(), std::__throw_out_of_range, "");
1599 procrustes(n, length() - pos);
1600 std::copy(begin() + pos + n, end(), begin() + pos);
1601 resize(length() - n);
1605 iterator erase(iterator position) {
1606 const size_type pos(position - begin());
1607 enforce(pos <= size(), std::__throw_out_of_range, "");
1609 return begin() + pos;
1612 iterator erase(iterator first, iterator last) {
1613 const size_type pos(first - begin());
1614 erase(pos, last - first);
1615 return begin() + pos;
1618 // Replaces at most n1 chars of *this, starting with pos1 with the
1620 basic_fbstring& replace(size_type pos1, size_type n1,
1621 const basic_fbstring& str) {
1622 return replace(pos1, n1, str.data(), str.size());
1625 // Replaces at most n1 chars of *this, starting with pos1,
1626 // with at most n2 chars of str starting with pos2
1627 basic_fbstring& replace(size_type pos1, size_type n1,
1628 const basic_fbstring& str,
1629 size_type pos2, size_type n2) {
1630 enforce(pos2 <= str.length(), std::__throw_out_of_range, "");
1631 return replace(pos1, n1, str.data() + pos2,
1632 std::min(n2, str.size() - pos2));
1635 // Replaces at most n1 chars of *this, starting with pos, with chars from s
1636 basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {
1637 return replace(pos, n1, s, traits_type::length(s));
1640 // Replaces at most n1 chars of *this, starting with pos, with n2
1643 // consolidated with
1645 // Replaces at most n1 chars of *this, starting with pos, with at
1646 // most n2 chars of str. str must have at least n2 chars.
1647 template <class StrOrLength, class NumOrChar>
1648 basic_fbstring& replace(size_type pos, size_type n1,
1649 StrOrLength s_or_n2, NumOrChar n_or_c) {
1650 Invariant checker(*this);
1652 enforce(pos <= size(), std::__throw_out_of_range, "");
1653 procrustes(n1, length() - pos);
1654 const iterator b = begin() + pos;
1655 return replace(b, b + n1, s_or_n2, n_or_c);
1658 basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {
1659 return replace(i1, i2, str.data(), str.length());
1662 basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {
1663 return replace(i1, i2, s, traits_type::length(s));
1667 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1668 const value_type* s, size_type n,
1671 assert(begin() <= i1 && i1 <= end());
1672 assert(begin() <= i2 && i2 <= end());
1673 return replace(i1, i2, s, s + n);
1676 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1677 size_type n2, value_type c, Selector<1>) {
1678 const size_type n1 = i2 - i1;
1680 std::fill(i1, i1 + n2, c);
1683 std::fill(i1, i2, c);
1684 insert(i2, n2 - n1, c);
1690 template <class InputIter>
1691 basic_fbstring& replaceImplDiscr(iterator i1, iterator i2,
1692 InputIter b, InputIter e,
1694 replaceImpl(i1, i2, b, e,
1695 typename std::iterator_traits<InputIter>::iterator_category());
1700 template <class FwdIterator>
1701 bool replaceAliased(iterator i1, iterator i2,
1702 FwdIterator s1, FwdIterator s2, std::false_type) {
1706 template <class FwdIterator>
1707 bool replaceAliased(iterator i1, iterator i2,
1708 FwdIterator s1, FwdIterator s2, std::true_type) {
1709 static const std::less_equal<const value_type*> le =
1710 std::less_equal<const value_type*>();
1711 const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());
1715 // Aliased replace, copy to new string
1716 basic_fbstring temp;
1717 temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));
1718 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,
1731 std::integral_constant<bool,
1732 std::is_same<FwdIterator, iterator>::value ||
1733 std::is_same<FwdIterator, const_iterator>::value>())) {
1737 auto const n1 = i2 - i1;
1739 auto const n2 = std::distance(s1, s2);
1744 std::copy(s1, s2, i1);
1748 fbstring_detail::copy_n(s1, n1, i1);
1749 std::advance(s1, n1);
1755 template <class InputIterator>
1756 void replaceImpl(iterator i1, iterator i2,
1757 InputIterator b, InputIterator e, std::input_iterator_tag) {
1758 basic_fbstring temp(begin(), i1);
1759 temp.append(b, e).append(i2, end());
1764 template <class T1, class T2>
1765 basic_fbstring& replace(iterator i1, iterator i2,
1766 T1 first_or_n_or_s, T2 last_or_c_or_n) {
1768 num1 = std::numeric_limits<T1>::is_specialized,
1769 num2 = std::numeric_limits<T2>::is_specialized;
1770 return replaceImplDiscr(
1771 i1, i2, first_or_n_or_s, last_or_c_or_n,
1772 Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>());
1775 size_type copy(value_type* s, size_type n, size_type pos = 0) const {
1776 enforce(pos <= size(), std::__throw_out_of_range, "");
1777 procrustes(n, size() - pos);
1779 fbstring_detail::pod_copy(
1786 void swap(basic_fbstring& rhs) {
1787 store_.swap(rhs.store_);
1790 const value_type* c_str() const {
1791 return store_.c_str();
1794 const value_type* data() const { return c_str(); }
1796 allocator_type get_allocator() const {
1797 return allocator_type();
1800 size_type find(const basic_fbstring& str, size_type pos = 0) const {
1801 return find(str.data(), pos, str.length());
1804 size_type find(const value_type* needle, const size_type pos,
1805 const size_type nsize) const {
1806 if (!nsize) return pos;
1807 auto const size = this->size();
1808 // nsize + pos can overflow (eg pos == npos), guard against that by checking
1809 // that nsize + pos does not wrap around.
1810 if (nsize + pos > size || nsize + pos < pos) return npos;
1811 // Don't use std::search, use a Boyer-Moore-like trick by comparing
1812 // the last characters first
1813 auto const haystack = data();
1814 auto const nsize_1 = nsize - 1;
1815 auto const lastNeedle = needle[nsize_1];
1817 // Boyer-Moore skip value for the last char in the needle. Zero is
1818 // not a valid value; skip will be computed the first time it's
1822 const E * i = haystack + pos;
1823 auto iEnd = haystack + size - nsize_1;
1826 // Boyer-Moore: match the last element in the needle
1827 while (i[nsize_1] != lastNeedle) {
1833 // Here we know that the last char matches
1834 // Continue in pedestrian mode
1835 for (size_t j = 0; ; ) {
1837 if (i[j] != needle[j]) {
1838 // Not found, we can skip
1839 // Compute the skip value lazily
1842 while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {
1849 // Check if done searching
1852 return i - haystack;
1859 size_type find(const value_type* s, size_type pos = 0) const {
1860 return find(s, pos, traits_type::length(s));
1863 size_type find (value_type c, size_type pos = 0) const {
1864 return find(&c, pos, 1);
1867 size_type rfind(const basic_fbstring& str, size_type pos = npos) const {
1868 return rfind(str.data(), pos, str.length());
1871 size_type rfind(const value_type* s, size_type pos, size_type n) const {
1872 if (n > length()) return npos;
1873 pos = std::min(pos, length() - n);
1874 if (n == 0) return pos;
1876 const_iterator i(begin() + pos);
1878 if (traits_type::eq(*i, *s)
1879 && traits_type::compare(&*i, s, n) == 0) {
1882 if (i == begin()) break;
1887 size_type rfind(const value_type* s, size_type pos = npos) const {
1888 return rfind(s, pos, traits_type::length(s));
1891 size_type rfind(value_type c, size_type pos = npos) const {
1892 return rfind(&c, pos, 1);
1895 size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {
1896 return find_first_of(str.data(), pos, str.length());
1899 size_type find_first_of(const value_type* s,
1900 size_type pos, size_type n) const {
1901 if (pos > length() || n == 0) return npos;
1902 const_iterator i(begin() + pos),
1904 for (; i != finish; ++i) {
1905 if (traits_type::find(s, n, *i) != 0) {
1912 size_type find_first_of(const value_type* s, size_type pos = 0) const {
1913 return find_first_of(s, pos, traits_type::length(s));
1916 size_type find_first_of(value_type c, size_type pos = 0) const {
1917 return find_first_of(&c, pos, 1);
1920 size_type find_last_of (const basic_fbstring& str,
1921 size_type pos = npos) const {
1922 return find_last_of(str.data(), pos, str.length());
1925 size_type find_last_of (const value_type* s, size_type pos,
1926 size_type n) const {
1927 if (!empty() && n > 0) {
1928 pos = std::min(pos, length() - 1);
1929 const_iterator i(begin() + pos);
1931 if (traits_type::find(s, n, *i) != 0) {
1934 if (i == begin()) break;
1940 size_type find_last_of (const value_type* s,
1941 size_type pos = npos) const {
1942 return find_last_of(s, pos, traits_type::length(s));
1945 size_type find_last_of (value_type c, size_type pos = npos) const {
1946 return find_last_of(&c, pos, 1);
1949 size_type find_first_not_of(const basic_fbstring& str,
1950 size_type pos = 0) const {
1951 return find_first_not_of(str.data(), pos, str.size());
1954 size_type find_first_not_of(const value_type* s, size_type pos,
1955 size_type n) const {
1956 if (pos < length()) {
1960 for (; i != finish; ++i) {
1961 if (traits_type::find(s, n, *i) == 0) {
1969 size_type find_first_not_of(const value_type* s,
1970 size_type pos = 0) const {
1971 return find_first_not_of(s, pos, traits_type::length(s));
1974 size_type find_first_not_of(value_type c, size_type pos = 0) const {
1975 return find_first_not_of(&c, pos, 1);
1978 size_type find_last_not_of(const basic_fbstring& str,
1979 size_type pos = npos) const {
1980 return find_last_not_of(str.data(), pos, str.length());
1983 size_type find_last_not_of(const value_type* s, size_type pos,
1984 size_type n) const {
1985 if (!this->empty()) {
1986 pos = std::min(pos, size() - 1);
1987 const_iterator i(begin() + pos);
1989 if (traits_type::find(s, n, *i) == 0) {
1992 if (i == begin()) break;
1998 size_type find_last_not_of(const value_type* s,
1999 size_type pos = npos) const {
2000 return find_last_not_of(s, pos, traits_type::length(s));
2003 size_type find_last_not_of (value_type c, size_type pos = npos) const {
2004 return find_last_not_of(&c, pos, 1);
2007 basic_fbstring substr(size_type pos = 0, size_type n = npos) const {
2008 enforce(pos <= size(), std::__throw_out_of_range, "");
2009 return basic_fbstring(data() + pos, std::min(n, size() - pos));
2012 int compare(const basic_fbstring& str) const {
2013 // FIX due to Goncalo N M de Carvalho July 18, 2005
2014 return compare(0, size(), str);
2017 int compare(size_type pos1, size_type n1,
2018 const basic_fbstring& str) const {
2019 return compare(pos1, n1, str.data(), str.size());
2022 int compare(size_type pos1, size_type n1,
2023 const value_type* s) const {
2024 return compare(pos1, n1, s, traits_type::length(s));
2027 int compare(size_type pos1, size_type n1,
2028 const value_type* s, size_type n2) const {
2029 enforce(pos1 <= size(), std::__throw_out_of_range, "");
2030 procrustes(n1, size() - pos1);
2031 // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!
2032 const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));
2033 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2036 int compare(size_type pos1, size_type n1,
2037 const basic_fbstring& str,
2038 size_type pos2, size_type n2) const {
2039 enforce(pos2 <= str.size(), std::__throw_out_of_range, "");
2040 return compare(pos1, n1, str.data() + pos2,
2041 std::min(n2, str.size() - pos2));
2044 // Code from Jean-Francois Bastien (03/26/2007)
2045 int compare(const value_type* s) const {
2046 // Could forward to compare(0, size(), s, traits_type::length(s))
2047 // but that does two extra checks
2048 const size_type n1(size()), n2(traits_type::length(s));
2049 const int r = traits_type::compare(data(), s, std::min(n1, n2));
2050 return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;
2058 // non-member functions
2060 template <typename E, class T, class A, class S>
2062 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2063 const basic_fbstring<E, T, A, S>& rhs) {
2065 basic_fbstring<E, T, A, S> result;
2066 result.reserve(lhs.size() + rhs.size());
2067 result.append(lhs).append(rhs);
2068 return std::move(result);
2072 template <typename E, class T, class A, class S>
2074 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2075 const basic_fbstring<E, T, A, S>& rhs) {
2076 return std::move(lhs.append(rhs));
2080 template <typename E, class T, class A, class S>
2082 basic_fbstring<E, T, A, S> operator+(const basic_fbstring<E, T, A, S>& lhs,
2083 basic_fbstring<E, T, A, S>&& rhs) {
2084 if (rhs.capacity() >= lhs.size() + rhs.size()) {
2085 // Good, at least we don't need to reallocate
2086 return std::move(rhs.insert(0, lhs));
2088 // Meh, no go. Forward to operator+(const&, const&).
2089 auto const& rhsC = rhs;
2094 template <typename E, class T, class A, class S>
2096 basic_fbstring<E, T, A, S> operator+(basic_fbstring<E, T, A, S>&& lhs,
2097 basic_fbstring<E, T, A, S>&& rhs) {
2098 return std::move(lhs.append(rhs));
2101 template <typename E, class T, class A, class S>
2103 basic_fbstring<E, T, A, S> operator+(
2104 const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2105 const basic_fbstring<E, T, A, S>& rhs) {
2107 basic_fbstring<E, T, A, S> result;
2108 const typename basic_fbstring<E, T, A, S>::size_type len =
2109 basic_fbstring<E, T, A, S>::traits_type::length(lhs);
2110 result.reserve(len + rhs.size());
2111 result.append(lhs, len).append(rhs);
2115 template <typename E, class T, class A, class S>
2117 basic_fbstring<E, T, A, S> operator+(
2118 typename basic_fbstring<E, T, A, S>::value_type lhs,
2119 const basic_fbstring<E, T, A, S>& rhs) {
2121 basic_fbstring<E, T, A, S> result;
2122 result.reserve(1 + rhs.size());
2123 result.push_back(lhs);
2128 template <typename E, class T, class A, class S>
2130 basic_fbstring<E, T, A, S> operator+(
2131 const basic_fbstring<E, T, A, S>& lhs,
2132 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2134 typedef typename basic_fbstring<E, T, A, S>::size_type size_type;
2135 typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;
2137 basic_fbstring<E, T, A, S> result;
2138 const size_type len = traits_type::length(rhs);
2139 result.reserve(lhs.size() + len);
2140 result.append(lhs).append(rhs, len);
2144 template <typename E, class T, class A, class S>
2146 basic_fbstring<E, T, A, S> operator+(
2147 const basic_fbstring<E, T, A, S>& lhs,
2148 typename basic_fbstring<E, T, A, S>::value_type rhs) {
2150 basic_fbstring<E, T, A, S> result;
2151 result.reserve(lhs.size() + 1);
2153 result.push_back(rhs);
2157 template <typename E, class T, class A, class S>
2159 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2160 const basic_fbstring<E, T, A, S>& rhs) {
2161 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; }
2163 template <typename E, class T, class A, class S>
2165 bool operator==(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2166 const basic_fbstring<E, T, A, S>& rhs) {
2167 return rhs == lhs; }
2169 template <typename E, class T, class A, class S>
2171 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2172 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2173 return lhs.compare(rhs) == 0; }
2175 template <typename E, class T, class A, class S>
2177 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2178 const basic_fbstring<E, T, A, S>& rhs) {
2179 return !(lhs == rhs); }
2181 template <typename E, class T, class A, class S>
2183 bool operator!=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2184 const basic_fbstring<E, T, A, S>& rhs) {
2185 return !(lhs == rhs); }
2187 template <typename E, class T, class A, class S>
2189 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2190 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2191 return !(lhs == rhs); }
2193 template <typename E, class T, class A, class S>
2195 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2196 const basic_fbstring<E, T, A, S>& rhs) {
2197 return lhs.compare(rhs) < 0; }
2199 template <typename E, class T, class A, class S>
2201 bool operator<(const basic_fbstring<E, T, A, S>& lhs,
2202 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2203 return lhs.compare(rhs) < 0; }
2205 template <typename E, class T, class A, class S>
2207 bool operator<(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2208 const basic_fbstring<E, T, A, S>& rhs) {
2209 return rhs.compare(lhs) > 0; }
2211 template <typename E, class T, class A, class S>
2213 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2214 const basic_fbstring<E, T, A, S>& rhs) {
2217 template <typename E, class T, class A, class S>
2219 bool operator>(const basic_fbstring<E, T, A, S>& lhs,
2220 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2223 template <typename E, class T, class A, class S>
2225 bool operator>(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2226 const basic_fbstring<E, T, A, S>& rhs) {
2229 template <typename E, class T, class A, class S>
2231 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2232 const basic_fbstring<E, T, A, S>& rhs) {
2233 return !(rhs < lhs); }
2235 template <typename E, class T, class A, class S>
2237 bool operator<=(const basic_fbstring<E, T, A, S>& lhs,
2238 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2239 return !(rhs < lhs); }
2241 template <typename E, class T, class A, class S>
2243 bool operator<=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2244 const basic_fbstring<E, T, A, S>& rhs) {
2245 return !(rhs < lhs); }
2247 template <typename E, class T, class A, class S>
2249 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2250 const basic_fbstring<E, T, A, S>& rhs) {
2251 return !(lhs < rhs); }
2253 template <typename E, class T, class A, class S>
2255 bool operator>=(const basic_fbstring<E, T, A, S>& lhs,
2256 const typename basic_fbstring<E, T, A, S>::value_type* rhs) {
2257 return !(lhs < rhs); }
2259 template <typename E, class T, class A, class S>
2261 bool operator>=(const typename basic_fbstring<E, T, A, S>::value_type* lhs,
2262 const basic_fbstring<E, T, A, S>& rhs) {
2263 return !(lhs < rhs);
2267 template <typename E, class T, class A, class S>
2268 void swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {
2272 // TODO: make this faster.
2273 template <typename E, class T, class A, class S>
2276 typename basic_fbstring<E, T, A, S>::value_type,
2277 typename basic_fbstring<E, T, A, S>::traits_type>&
2279 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2280 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2281 basic_fbstring<E, T, A, S>& str) {
2282 typename std::basic_istream<E, T>::sentry sentry(is);
2283 typedef std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2284 typename basic_fbstring<E, T, A, S>::traits_type>
2286 typedef typename __istream_type::ios_base __ios_base;
2287 size_t extracted = 0;
2288 auto err = __ios_base::goodbit;
2290 auto n = is.width();
2295 auto got = is.rdbuf()->sgetc();
2296 for (; extracted != n && got != T::eof() && !isspace(got); ++extracted) {
2297 // Whew. We get to store this guy
2299 got = is.rdbuf()->snextc();
2301 if (got == T::eof()) {
2302 err |= __ios_base::eofbit;
2307 err |= __ios_base::failbit;
2315 template <typename E, class T, class A, class S>
2317 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2318 typename basic_fbstring<E, T, A, S>::traits_type>&
2320 std::basic_ostream<typename basic_fbstring<E, T, A, S>::value_type,
2321 typename basic_fbstring<E, T, A, S>::traits_type>& os,
2322 const basic_fbstring<E, T, A, S>& str) {
2324 typename std::basic_ostream<
2325 typename basic_fbstring<E, T, A, S>::value_type,
2326 typename basic_fbstring<E, T, A, S>::traits_type>::sentry __s(os);
2328 typedef std::ostreambuf_iterator<
2329 typename basic_fbstring<E, T, A, S>::value_type,
2330 typename basic_fbstring<E, T, A, S>::traits_type> _Ip;
2331 size_t __len = str.size();
2333 (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
2334 if (__pad_and_output(_Ip(os),
2336 __left ? str.data() + __len : str.data(),
2339 os.fill()).failed()) {
2340 os.setstate(std::ios_base::badbit | std::ios_base::failbit);
2344 std::__ostream_insert(os, str.data(), str.size());
2349 #ifndef _LIBSTDCXX_FBSTRING
2351 template <typename E, class T, class A, class S>
2353 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2354 typename basic_fbstring<E, T, A, S>::traits_type>&
2356 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2357 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2358 basic_fbstring<E, T, A, S>& str,
2359 typename basic_fbstring<E, T, A, S>::value_type delim) {
2360 // Use the nonstandard getdelim()
2364 // This looks quadratic but it really depends on realloc
2365 auto const newSize = size + 128;
2366 buf = static_cast<char*>(checkedRealloc(buf, newSize));
2367 is.getline(buf + size, newSize - size, delim);
2368 if (is.bad() || is.eof() || !is.fail()) {
2369 // done by either failure, end of file, or normal read
2370 size += std::strlen(buf + size);
2373 // Here we have failed due to too short a buffer
2374 // Minus one to discount the terminating '\0'
2376 assert(buf[size] == 0);
2377 // Clear the error so we can continue reading
2380 basic_fbstring<E, T, A, S> result(buf, size, size + 1,
2381 AcquireMallocatedString());
2386 template <typename E, class T, class A, class S>
2388 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2389 typename basic_fbstring<E, T, A, S>::traits_type>&
2391 std::basic_istream<typename basic_fbstring<E, T, A, S>::value_type,
2392 typename basic_fbstring<E, T, A, S>::traits_type>& is,
2393 basic_fbstring<E, T, A, S>& str) {
2394 // Just forward to the version with a delimiter
2395 return getline(is, str, '\n');
2400 template <typename E1, class T, class A, class S>
2401 const typename basic_fbstring<E1, T, A, S>::size_type
2402 basic_fbstring<E1, T, A, S>::npos =
2403 static_cast<typename basic_fbstring<E1, T, A, S>::size_type>(-1);
2405 #ifndef _LIBSTDCXX_FBSTRING
2406 // basic_string compatibility routines
2408 template <typename E, class T, class A, class S>
2410 bool operator==(const basic_fbstring<E, T, A, S>& lhs,
2411 const std::string& rhs) {
2412 return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;
2415 template <typename E, class T, class A, class S>
2417 bool operator==(const std::string& lhs,
2418 const basic_fbstring<E, T, A, S>& rhs) {
2422 template <typename E, class T, class A, class S>
2424 bool operator!=(const basic_fbstring<E, T, A, S>& lhs,
2425 const std::string& rhs) {
2426 return !(lhs == rhs);
2429 template <typename E, class T, class A, class S>
2431 bool operator!=(const std::string& lhs,
2432 const basic_fbstring<E, T, A, S>& rhs) {
2433 return !(lhs == rhs);
2436 #if !defined(_LIBSTDCXX_FBSTRING)
2437 typedef basic_fbstring<char> fbstring;
2440 // fbstring is relocatable
2441 template <class T, class R, class A, class S>
2442 FOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);
2445 _GLIBCXX_END_NAMESPACE_VERSION
2448 } // namespace folly
2450 #ifndef _LIBSTDCXX_FBSTRING
2452 // Hash functions to make fbstring usable with e.g. hash_map
2454 // Handle interaction with different C++ standard libraries, which
2455 // expect these types to be in different namespaces.
2459 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2460 size_t operator()(const folly::basic_fbstring<C> & s) const {
2461 return hash<const C*>::operator()(s.c_str());
2466 struct hash< ::folly::fbstring> {
2467 size_t operator()(const ::folly::fbstring& s) const {
2468 return ::folly::hash::fnv32_buf(s.data(), s.size());
2474 #if defined(_GLIBCXX_SYMVER) && !defined(__BIONIC__)
2475 namespace __gnu_cxx {
2478 struct hash<folly::basic_fbstring<C> > : private hash<const C*> {
2479 size_t operator()(const folly::basic_fbstring<C> & s) const {
2480 return hash<const C*>::operator()(s.c_str());
2485 struct hash< ::folly::fbstring> {
2486 size_t operator()(const ::folly::fbstring& s) const {
2487 return ::folly::hash::fnv32_buf(s.data(), s.size());
2492 #endif // _GLIBCXX_SYMVER && !__BIONIC__
2493 #endif // _LIBSTDCXX_FBSTRING
2495 #pragma GCC diagnostic pop
2497 #undef FBSTRING_DISABLE_ADDRESS_SANITIZER
2499 #undef FBSTRING_LIKELY
2500 #undef FBSTRING_UNLIKELY
2502 #endif // FOLLY_BASE_FBSTRING_H_