1/*
2 * Copyright 2016-present Facebook, Inc.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17// @author: Eric Niebler (eniebler)
18// Fixed-size string type, for constexpr string handling.
19
20#pragma once
21
22#include <cassert>
23#include <cstddef>
24#include <initializer_list>
25#include <iosfwd>
26#include <stdexcept>
27#include <string>
28#include <type_traits>
29#include <utility>
30
31#include <folly/ConstexprMath.h>
32#include <folly/Portability.h>
33#include <folly/Range.h>
34#include <folly/Utility.h>
35#include <folly/lang/Exception.h>
36#include <folly/lang/Ordering.h>
37#include <folly/portability/Constexpr.h>
38
39namespace folly {
40
41template <class Char, std::size_t N>
42class BasicFixedString;
43
44template <std::size_t N>
45using FixedString = BasicFixedString<char, N>;
46
47namespace detail {
48namespace fixedstring {
49
50// This is a template so that the class static npos can be defined in the
51// header.
52template <class = void>
53struct FixedStringBase_ {
54 static constexpr std::size_t npos = static_cast<std::size_t>(-1);
55};
56
57template <class Void>
58constexpr std::size_t FixedStringBase_<Void>::npos;
59
60using FixedStringBase = FixedStringBase_<>;
61
62// Intentionally NOT constexpr. By making this not constexpr, we make
63// checkOverflow below ill-formed in a constexpr context when the condition
64// it's testing for fails. In this way, precondition violations are reported
65// at compile-time instead of at runtime.
66[[noreturn]] inline void assertOutOfBounds() {
67 assert(!"Array index out of bounds in BasicFixedString");
68 throw_exception<std::out_of_range>(
69 "Array index out of bounds in BasicFixedString");
70}
71
72constexpr std::size_t checkOverflow(std::size_t i, std::size_t max) {
73 return i <= max ? i : (void(assertOutOfBounds()), max);
74}
75
76constexpr std::size_t checkOverflowOrNpos(std::size_t i, std::size_t max) {
77 return i == FixedStringBase::npos
78 ? max
79 : (i <= max ? i : (void(assertOutOfBounds()), max));
80}
81
82// Intentionally NOT constexpr. See note above for assertOutOfBounds
83[[noreturn]] inline void assertNotNullTerminated() noexcept {
84 assert(!"Non-null terminated string used to initialize a BasicFixedString");
85 std::terminate(); // Fail hard, fail fast.
86}
87
88// Parsing help for human readers: the following is a constexpr noexcept
89// function that accepts a reference to an array as a parameter and returns
90// a reference to the same array.
91template <class Char, std::size_t N>
92constexpr const Char (&checkNullTerminated(const Char (&a)[N]) noexcept)[N] {
93 // Strange decltype(a)(a) used to make MSVC happy.
94 return a[N - 1u] == Char(0)
95#ifndef NDEBUG
96 // In Debug mode, guard against embedded nulls:
97 && N - 1u == folly::detail::constexpr_strlen_internal(a, 0u)
98#endif
99 ? decltype(a)(a)
100 : (assertNotNullTerminated(), decltype(a)(a));
101}
102
103// Rather annoyingly, GCC's -Warray-bounds warning issues false positives for
104// this code. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61971
105#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
106#pragma GCC diagnostic push
107#pragma GCC diagnostic ignored "-Warray-bounds"
108#endif
109
110template <class Left, class Right>
111constexpr ordering compare_(
112 const Left& left,
113 std::size_t left_pos,
114 std::size_t left_size,
115 const Right& right,
116 std::size_t right_pos,
117 std::size_t right_size) noexcept {
118 return left_pos == left_size
119 ? (right_pos == right_size ? ordering::eq : ordering::lt)
120 : (right_pos == right_size ? ordering::gt
121 : (left[left_pos] < right[right_pos]
122 ? ordering::lt
123 : (left[left_pos] > right[right_pos]
124 ? ordering::gt
125 : fixedstring::compare_(
126 left,
127 left_pos + 1u,
128 left_size,
129 right,
130 right_pos + 1u,
131 right_size))));
132}
133
134template <class Left, class Right>
135constexpr bool equal_(
136 const Left& left,
137 std::size_t left_size,
138 const Right& right,
139 std::size_t right_size) noexcept {
140 return left_size == right_size &&
141 ordering::eq == compare_(left, 0u, left_size, right, 0u, right_size);
142}
143
144template <class Char, class Left, class Right>
145constexpr Char char_at_(
146 const Left& left,
147 std::size_t left_count,
148 const Right& right,
149 std::size_t right_count,
150 std::size_t i) noexcept {
151 return i < left_count
152 ? left[i]
153 : i < (left_count + right_count) ? right[i - left_count] : Char(0);
154}
155
156template <class Char, class Left, class Right>
157constexpr Char char_at_(
158 const Left& left,
159 std::size_t left_size,
160 std::size_t left_pos,
161 std::size_t left_count,
162 const Right& right,
163 std::size_t right_pos,
164 std::size_t right_count,
165 std::size_t i) noexcept {
166 return i < left_pos
167 ? left[i]
168 : (i < right_count + left_pos ? right[i - left_pos + right_pos]
169 : (i < left_size - left_count + right_count
170 ? left[i - right_count + left_count]
171 : Char(0)));
172}
173
174template <class Left, class Right>
175constexpr bool find_at_(
176 const Left& left,
177 const Right& right,
178 std::size_t pos,
179 std::size_t count) noexcept {
180 return 0u == count ||
181 (left[pos + count - 1u] == right[count - 1u] &&
182 find_at_(left, right, pos, count - 1u));
183}
184
185template <class Char, class Right>
186constexpr bool
187find_one_of_at_(Char ch, const Right& right, std::size_t pos) noexcept {
188 return 0u != pos &&
189 (ch == right[pos - 1u] || find_one_of_at_(ch, right, pos - 1u));
190}
191
192template <class Left, class Right>
193constexpr std::size_t find_(
194 const Left& left,
195 std::size_t left_size,
196 const Right& right,
197 std::size_t pos,
198 std::size_t count) noexcept {
199 return find_at_(left, right, pos, count) ? pos
200 : left_size <= pos + count
201 ? FixedStringBase::npos
202 : find_(left, left_size, right, pos + 1u, count);
203}
204
205template <class Left, class Right>
206constexpr std::size_t rfind_(
207 const Left& left,
208 const Right& right,
209 std::size_t pos,
210 std::size_t count) noexcept {
211 return find_at_(left, right, pos, count)
212 ? pos
213 : 0u == pos ? FixedStringBase::npos
214 : rfind_(left, right, pos - 1u, count);
215}
216
217template <class Left, class Right>
218constexpr std::size_t find_first_of_(
219 const Left& left,
220 std::size_t left_size,
221 const Right& right,
222 std::size_t pos,
223 std::size_t count) noexcept {
224 return find_one_of_at_(left[pos], right, count) ? pos
225 : left_size <= pos + 1u
226 ? FixedStringBase::npos
227 : find_first_of_(left, left_size, right, pos + 1u, count);
228}
229
230template <class Left, class Right>
231constexpr std::size_t find_first_not_of_(
232 const Left& left,
233 std::size_t left_size,
234 const Right& right,
235 std::size_t pos,
236 std::size_t count) noexcept {
237 return !find_one_of_at_(left[pos], right, count) ? pos
238 : left_size <= pos + 1u
239 ? FixedStringBase::npos
240 : find_first_not_of_(left, left_size, right, pos + 1u, count);
241}
242
243template <class Left, class Right>
244constexpr std::size_t find_last_of_(
245 const Left& left,
246 const Right& right,
247 std::size_t pos,
248 std::size_t count) noexcept {
249 return find_one_of_at_(left[pos], right, count)
250 ? pos
251 : 0u == pos ? FixedStringBase::npos
252 : find_last_of_(left, right, pos - 1u, count);
253}
254
255template <class Left, class Right>
256constexpr std::size_t find_last_not_of_(
257 const Left& left,
258 const Right& right,
259 std::size_t pos,
260 std::size_t count) noexcept {
261 return !find_one_of_at_(left[pos], right, count)
262 ? pos
263 : 0u == pos ? FixedStringBase::npos
264 : find_last_not_of_(left, right, pos - 1u, count);
265}
266
267struct Helper {
268 template <class Char, class Left, class Right, std::size_t... Is>
269 static constexpr BasicFixedString<Char, sizeof...(Is)> concat_(
270 const Left& left,
271 std::size_t left_count,
272 const Right& right,
273 std::size_t right_count,
274 folly::index_sequence<Is...> is) noexcept {
275 return {left, left_count, right, right_count, is};
276 }
277
278 template <class Char, class Left, class Right, std::size_t... Is>
279 static constexpr BasicFixedString<Char, sizeof...(Is)> replace_(
280 const Left& left,
281 std::size_t left_size,
282 std::size_t left_pos,
283 std::size_t left_count,
284 const Right& right,
285 std::size_t right_pos,
286 std::size_t right_count,
287 folly::index_sequence<Is...> is) noexcept {
288 return {left,
289 left_size,
290 left_pos,
291 left_count,
292 right,
293 right_pos,
294 right_count,
295 is};
296 }
297
298 template <class Char, std::size_t N>
299 static constexpr const Char (
300 &data_(const BasicFixedString<Char, N>& that) noexcept)[N + 1u] {
301 return that.data_;
302 }
303};
304
305#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4
306#pragma GCC diagnostic pop
307#endif
308
309template <class T>
310FOLLY_CPP14_CONSTEXPR void constexpr_swap(T& a, T& b) noexcept(
311 noexcept(a = T(std::move(a)))) {
312 T tmp((std::move(a)));
313 a = std::move(b);
314 b = std::move(tmp);
315}
316
317// For constexpr reverse iteration over a BasicFixedString
318template <class T>
319struct ReverseIterator {
320 private:
321 T* p_ = nullptr;
322 struct dummy_ {
323 T* p_ = nullptr;
324 };
325 using other = typename std::conditional<
326 std::is_const<T>::value,
327 ReverseIterator<typename std::remove_const<T>::type>,
328 dummy_>::type;
329
330 public:
331 using value_type = typename std::remove_const<T>::type;
332 using reference = T&;
333 using pointer = T*;
334 using difference_type = std::ptrdiff_t;
335 using iterator_category = std::random_access_iterator_tag;
336
337 constexpr ReverseIterator() = default;
338 constexpr ReverseIterator(const ReverseIterator&) = default;
339 FOLLY_CPP14_CONSTEXPR ReverseIterator& operator=(const ReverseIterator&) =
340 default;
341 constexpr explicit ReverseIterator(T* p) noexcept : p_(p) {}
342 constexpr /* implicit */ ReverseIterator(const other& that) noexcept
343 : p_(that.p_) {}
344 friend constexpr bool operator==(
345 ReverseIterator a,
346 ReverseIterator b) noexcept {
347 return a.p_ == b.p_;
348 }
349 friend constexpr bool operator!=(
350 ReverseIterator a,
351 ReverseIterator b) noexcept {
352 return !(a == b);
353 }
354 constexpr reference operator*() const {
355 return *(p_ - 1);
356 }
357 FOLLY_CPP14_CONSTEXPR ReverseIterator& operator++() noexcept {
358 --p_;
359 return *this;
360 }
361 FOLLY_CPP14_CONSTEXPR ReverseIterator operator++(int) noexcept {
362 auto tmp(*this);
363 --p_;
364 return tmp;
365 }
366 FOLLY_CPP14_CONSTEXPR ReverseIterator& operator--() noexcept {
367 ++p_;
368 return *this;
369 }
370 FOLLY_CPP14_CONSTEXPR ReverseIterator operator--(int) noexcept {
371 auto tmp(*this);
372 ++p_;
373 return tmp;
374 }
375 FOLLY_CPP14_CONSTEXPR ReverseIterator& operator+=(std::ptrdiff_t i) noexcept {
376 p_ -= i;
377 return *this;
378 }
379 friend constexpr ReverseIterator operator+(
380 std::ptrdiff_t i,
381 ReverseIterator that) noexcept {
382 return ReverseIterator{that.p_ - i};
383 }
384 friend constexpr ReverseIterator operator+(
385 ReverseIterator that,
386 std::ptrdiff_t i) noexcept {
387 return ReverseIterator{that.p_ - i};
388 }
389 FOLLY_CPP14_CONSTEXPR ReverseIterator& operator-=(std::ptrdiff_t i) noexcept {
390 p_ += i;
391 return *this;
392 }
393 friend constexpr ReverseIterator operator-(
394 ReverseIterator that,
395 std::ptrdiff_t i) noexcept {
396 return ReverseIterator{that.p_ + i};
397 }
398 friend constexpr std::ptrdiff_t operator-(
399 ReverseIterator a,
400 ReverseIterator b) noexcept {
401 return b.p_ - a.p_;
402 }
403 constexpr reference operator[](std::ptrdiff_t i) const noexcept {
404 return *(*this + i);
405 }
406};
407
408} // namespace fixedstring
409} // namespace detail
410
411// Defined in folly/hash/Hash.h
412std::uint32_t hsieh_hash32_buf(const void* buf, std::size_t len);
413
414/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *
415 * \class BasicFixedString
416 *
417 * \tparam Char The character type. Must be a scalar type.
418 * \tparam N The capacity and max size of string instances of this type.
419 *
420 * \brief A class for holding up to `N` characters of type `Char` that is
421 * amenable to `constexpr` string manipulation. It is guaranteed to not
422 * perform any dynamic allocation.
423 *
424 * `BasicFixedString` is a `std::string` work-alike that stores characters in an
425 * internal buffer. It has minor interface differences that make it easy to work
426 * with strings in a `constexpr` context.
427 *
428 * \par Example:
429 * \par
430 * \code
431 * constexpr auto hello = makeFixedString("hello"); // a FixedString<5>
432 * constexpr auto world = makeFixedString("world"); // a FixedString<5>
433 * constexpr auto hello_world = hello + ' ' + world + '!'; // a FixedString<12>
434 * static_assert(hello_world == "hello world!", "neato!");
435 * \endcode
436 * \par
437 * `FixedString<N>` is an alias for `BasicFixedString<char, N>`.
438 *
439 * \par Constexpr and In-place Mutation
440 * \par
441 * On a C++14 compiler, `BasicFixedString` supports the full `std::string`
442 * interface as `constexpr` member functions. On a C++11 compiler, the mutating
443 * members are not `constexpr`, but non-mutating alternatives, which create a
444 * new string, can be used instead. For example, instead of this:
445 * \par
446 * \code
447 * constexpr FixedString<10> replace_example_cpp14() {
448 * FixedString<10> test{"****"};
449 * test.replace(1, 2, "!!!!");
450 * return test; // returns "*!!!!*"
451 * }
452 * \endcode
453 * \par
454 * You might write this instead:
455 * \par
456 * \code
457 * constexpr FixedString<10> replace_example_cpp11() {
458 * // GNU compilers have an extension that make it possible to create
459 * // FixedString objects with a `""_fs` user-defined literal.
460 * using namespace folly;
461 * return makeFixedString("****").creplace(1, 2, "!!!!"); // "*!!!!*"
462 * }
463 * \endcode
464 *
465 * \par User-defined Literals
466 * Instead of using the `folly::makeFixedString` helper function, you can use
467 * a user-defined literal to make `FixedString` instances. The UDL feature of
468 * C++ has some limitations that make this less than ideal; you must tell the
469 * compiler roughly how many characters are in the string. The suffixes `_fs4`,
470 * `_fs8`, `_fs16`, `_fs32`, `_fs64`, and `_fs128` exist to create instances
471 * of types `FixedString<4>`, `FixedString<8>`, etc. For example:
472 * \par
473 * \code
474 * using namespace folly::string_literals;
475 * constexpr auto hello = "hello"_fs8; // A FixedString<8> containing "hello"
476 * \endcode
477 * \par
478 * See Error Handling below for what to expect when you try to exceed the
479 * capacity of a `FixedString` by storing too many characters in it.
480 * \par
481 * If your compiler supports GNU extensions, there is one additional suffix you
482 * can use: `_fs`. This suffix always creates `FixedString` objects of exactly
483 * the right size. For example:
484 * \par
485 * \code
486 * using namespace folly::string_literals;
487 * // NOTE: Only works on compilers with GNU extensions enabled. Clang and
488 * // gcc support this (-Wgnu-string-literal-operator-template):
489 * constexpr auto hello = "hello"_fs; // A FixedString<5> containing "hello"
490 * \endcode
491 *
492 * \par Error Handling:
493 * The capacity of a `BasicFixedString` is set at compile time. When the user
494 * asks the string to exceed its capacity, one of three things will happen,
495 * depending on the context:
496 *\par
497 * -# If the attempt is made while evaluating a constant expression, the
498 * program will fail to compile.
499 * -# Otherwise, if the program is being run in debug mode, it will `assert`.
500 * -# Otherwise, the failed operation will throw a `std::out_of_range`
501 * exception.
502 *\par
503 * This is also the case if an invalid offset is passed to any member function,
504 * or if `pop_back` or `cpop_back` is called on an empty `BasicFixedString`.
505 *
506 * Member functions documented as having preconditions will assert in Debug
507 * mode (`!defined(NDEBUG)`) on precondition failures. Those documented with
508 * \b Throws clauses will throw the specified exception on failure. Those with
509 * both a precondition and a \b Throws clause will assert in Debug and throw
510 * in Release mode.
511 */
512template <class Char, std::size_t N>
513class BasicFixedString : private detail::fixedstring::FixedStringBase {
514 private:
515 template <class, std::size_t>
516 friend class BasicFixedString;
517 friend struct detail::fixedstring::Helper;
518
519 // FUTURE: use constexpr_log2 to fold instantiations of BasicFixedString
520 // together. All BasicFixedString<C, N> instantiations could share the
521 // implementation of BasicFixedString<C, M>, where M is the next highest power
522 // of 2 after N.
523 //
524 // Also, because of alignment of the data_ and size_ members, N should never
525 // be smaller than `(alignof(std::size_t)/sizeof(C))-1` (-1 because of the
526 // null terminator). OR, create a specialization for BasicFixedString<C, 0u>
527 // that does not have a size_ member, since it is unnecessary.
528 Char data_[N + 1u]; // +1 for the null terminator
529 std::size_t size_; // Nbr of chars, not incl. null terminator. size_ <= N.
530
531 using Indices = folly::make_index_sequence<N>;
532
533 template <class That, std::size_t... Is>
534 constexpr BasicFixedString(
535 const That& that,
536 std::size_t size,
537 folly::index_sequence<Is...>,
538 std::size_t pos = 0,
539 std::size_t count = npos) noexcept
540 : data_{(Is < (size - pos) && Is < count ? that[Is + pos] : Char(0))...,
541 Char(0)},
542 size_{folly::constexpr_min(size - pos, count)} {}
543
544 template <std::size_t... Is>
545 constexpr BasicFixedString(
546 std::size_t count,
547 Char ch,
548 folly::index_sequence<Is...>) noexcept
549 : data_{((Is < count) ? ch : Char(0))..., Char(0)}, size_{count} {}
550
551 // Concatenation constructor
552 template <class Left, class Right, std::size_t... Is>
553 constexpr BasicFixedString(
554 const Left& left,
555 std::size_t left_size,
556 const Right& right,
557 std::size_t right_size,
558 folly::index_sequence<Is...>) noexcept
559 : data_{detail::fixedstring::char_at_<Char>(
560 left,
561 left_size,
562 right,
563 right_size,
564 Is)...,
565 Char(0)},
566 size_{left_size + right_size} {}
567
568 // Replace constructor
569 template <class Left, class Right, std::size_t... Is>
570 constexpr BasicFixedString(
571 const Left& left,
572 std::size_t left_size,
573 std::size_t left_pos,
574 std::size_t left_count,
575 const Right& right,
576 std::size_t right_pos,
577 std::size_t right_count,
578 folly::index_sequence<Is...>) noexcept
579 : data_{detail::fixedstring::char_at_<Char>(
580 left,
581 left_size,
582 left_pos,
583 left_count,
584 right,
585 right_pos,
586 right_count,
587 Is)...,
588 Char(0)},
589 size_{left_size - left_count + right_count} {}
590
591 public:
592 using size_type = std::size_t;
593 using difference_type = std::ptrdiff_t;
594 using reference = Char&;
595 using const_reference = const Char&;
596 using pointer = Char*;
597 using const_pointer = const Char*;
598 using iterator = Char*;
599 using const_iterator = const Char*;
600 using reverse_iterator = detail::fixedstring::ReverseIterator<Char>;
601 using const_reverse_iterator =
602 detail::fixedstring::ReverseIterator<const Char>;
603
604 using detail::fixedstring::FixedStringBase::npos;
605
606 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
607 * Default construct
608 * \post `size() == 0`
609 * \post `at(0) == Char(0)`
610 */
611 constexpr BasicFixedString() : data_{}, size_{} {}
612
613 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
614 * Copy construct
615 * \post `size() == that.size()`
616 * \post `0 == strncmp(data(), that.data(), size())`
617 * \post `at(size()) == Char(0)`
618 */
619 constexpr BasicFixedString(const BasicFixedString& /*that*/) = default;
620
621 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
622 * Construct from a differently-sized BasicFixedString
623 * \pre `that.size() <= N`
624 * \post `size() == that.size()`
625 * \post `0 == strncmp(data(), that.data(), size())`
626 * \post `at(size()) == Char(0)`
627 * \throw std::out_of_range when that.size() > N. When M <= N, this
628 * constructor will never throw.
629 * \note Conversions from larger-capacity BasicFixedString objects to smaller
630 * ones (`M > N`) are allowed as long as the *size()* of the source string
631 * is small enough.
632 */
633 template <std::size_t M>
634 constexpr /* implicit */ BasicFixedString(
635 const BasicFixedString<Char, M>& that) noexcept(M <= N)
636 : BasicFixedString{that, 0u, that.size_} {}
637
638 // Why is this deleted? To avoid confusion with the constructor that takes
639 // a const Char* and a count.
640 template <std::size_t M>
641 constexpr BasicFixedString(
642 const BasicFixedString<Char, M>& that,
643 std::size_t pos) noexcept(false) = delete;
644
645 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
646 * Construct from an BasicFixedString, an offset, and a count
647 * \param that The source string
648 * \param pos The starting position in `that`
649 * \param count The number of characters to copy. If `npos`, `count` is taken
650 * to be `that.size()-pos`.
651 * \pre `pos <= that.size()`
652 * \pre `count <= that.size()-pos && count <= N`
653 * \post `size() == count`
654 * \post `0 == strncmp(data(), that.data()+pos, size())`
655 * \post `at(size()) == Char(0)`
656 * \throw std::out_of_range when pos+count > that.size(), or when
657 * `count > N`
658 */
659 template <std::size_t M>
660 constexpr BasicFixedString(
661 const BasicFixedString<Char, M>& that,
662 std::size_t pos,
663 std::size_t count) noexcept(false)
664 : BasicFixedString{
665 that.data_,
666 that.size_,
667 folly::make_index_sequence<(M < N ? M : N)>{},
668 pos,
669 detail::fixedstring::checkOverflow(
670 detail::fixedstring::checkOverflowOrNpos(
671 count,
672 that.size_ -
673 detail::fixedstring::checkOverflow(pos, that.size_)),
674 N)} {}
675
676 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
677 * Construct from a string literal
678 * \pre `M-1 <= N`
679 * \pre `that[M-1] == Char(0)`
680 * \post `0 == strncmp(data(), that, M-1)`
681 * \post `size() == M-1`
682 * \post `at(size()) == Char(0)`
683 */
684 template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>
685 constexpr /* implicit */ BasicFixedString(const Char (&that)[M]) noexcept
686 : BasicFixedString{detail::fixedstring::checkNullTerminated(that),
687 M - 1u,
688 folly::make_index_sequence<M - 1u>{}} {}
689
690 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
691 * Construct from a `const Char*` and count
692 * \pre `that` points to an array of at least `count` characters.
693 * \pre `count <= N`
694 * \post `size() == count`
695 * \post `0 == strncmp(data(), that, size())`
696 * \post `at(size()) == Char(0)`
697 * \throw std::out_of_range when count > N
698 */
699 constexpr BasicFixedString(const Char* that, std::size_t count) noexcept(
700 false)
701 : BasicFixedString{that,
702 detail::fixedstring::checkOverflow(count, N),
703 Indices{}} {}
704
705 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
706 * Construct an BasicFixedString that contains `count` characters, all
707 * of which are `ch`.
708 * \pre `count <= N`
709 * \post `size() == count`
710 * \post `npos == find_first_not_of(ch)`
711 * \post `at(size()) == Char(0)`
712 * \throw std::out_of_range when count > N
713 */
714 constexpr BasicFixedString(std::size_t count, Char ch) noexcept(false)
715 : BasicFixedString{detail::fixedstring::checkOverflow(count, N),
716 ch,
717 Indices{}} {}
718
719 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
720 * Construct an BasicFixedString from a `std::initializer_list` of
721 * characters.
722 * \pre `il.size() <= N`
723 * \post `size() == count`
724 * \post `0 == strncmp(data(), il.begin(), size())`
725 * \post `at(size()) == Char(0)`
726 * \throw std::out_of_range when il.size() > N
727 */
728 constexpr BasicFixedString(std::initializer_list<Char> il) noexcept(false)
729 : BasicFixedString{il.begin(), il.size()} {}
730
731 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator=(
732 const BasicFixedString&) noexcept = default;
733
734 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
735 * Assign from a `BasicFixedString<Char, M>`.
736 * \pre `that.size() <= N`
737 * \post `size() == that.size()`
738 * \post `0 == strncmp(data(), that.begin(), size())`
739 * \post `at(size()) == Char(0)`
740 * \throw std::out_of_range when that.size() > N. When M <= N, this
741 * assignment operator will never throw.
742 * \note Assignments from larger-capacity BasicFixedString objects to smaller
743 * ones (`M > N`) are allowed as long as the *size* of the source string is
744 * small enough.
745 * \return `*this`
746 */
747 template <std::size_t M>
748 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator=(
749 const BasicFixedString<Char, M>& that) noexcept(M <= N) {
750 detail::fixedstring::checkOverflow(that.size_, N);
751 size_ = that.copy(data_, that.size_);
752 data_[size_] = Char(0);
753 return *this;
754 }
755
756 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
757 * Assign from a null-terminated array of characters.
758 * \pre `M < N`
759 * \pre `that` has no embedded null characters
760 * \pre `that[M-1]==Char(0)`
761 * \post `size() == M-1`
762 * \post `0 == strncmp(data(), that, size())`
763 * \post `at(size()) == Char(0)`
764 * \return `*this`
765 */
766 template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>
767 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator=(
768 const Char (&that)[M]) noexcept {
769 return assign(detail::fixedstring::checkNullTerminated(that), M - 1u);
770 }
771
772 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
773 * Assign from an `initializer_list` of characters.
774 * \pre `il.size() <= N`
775 * \post `size() == il.size()`
776 * \post `0 == strncmp(data(), il.begin(), size())`
777 * \post `at(size()) == Char(0)`
778 * \throw std::out_of_range when il.size() > N
779 * \return `*this`
780 */
781 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator=(
782 std::initializer_list<Char> il) noexcept(false) {
783 detail::fixedstring::checkOverflow(il.size(), N);
784 for (std::size_t i = 0u; i < il.size(); ++i) {
785 data_[i] = il.begin()[i];
786 }
787 size_ = il.size();
788 data_[size_] = Char(0);
789 return *this;
790 }
791
792 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
793 * Conversion to folly::Range
794 * \return `Range<Char*>{begin(), end()}`
795 */
796 FOLLY_CPP14_CONSTEXPR Range<Char*> toRange() noexcept {
797 return {begin(), end()};
798 }
799
800 /**
801 * \overload
802 */
803 constexpr Range<const Char*> toRange() const noexcept {
804 return {begin(), end()};
805 }
806
807 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
808 * Conversion to std::basic_string<Char>
809 * \return `std::basic_string<Char>{begin(), end()}`
810 */
811 /* implicit */ operator std::basic_string<Char>() const noexcept(false) {
812 return std::basic_string<Char>{begin(), end()};
813 }
814
815 std::basic_string<Char> toStdString() const noexcept(false) {
816 return std::basic_string<Char>{begin(), end()};
817 }
818
819 // Think hard about whether this is a good idea. It's certainly better than
820 // an implicit conversion to `const Char*` since `delete "hi"_fs` will fail
821 // to compile. But it creates ambiguities when passing a FixedString to an
822 // API that has overloads for `const char*` and `folly::Range`, for instance.
823 // using ArrayType = Char[N];
824 // FOLLY_CPP14_CONSTEXPR /* implicit */ operator ArrayType&() noexcept {
825 // return data_;
826 // }
827
828 // using ConstArrayType = const Char[N];
829 // constexpr /* implicit */ operator ConstArrayType&() const noexcept {
830 // return data_;
831 // }
832
833 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
834 * Assigns a sequence of `count` characters of value `ch`.
835 * \param count The count of characters.
836 * \param ch
837 * \pre `count <= N`
838 * \post `size() == count`
839 * \post `npos == find_first_not_of(ch)`
840 * \post `at(size()) == Char(0)`
841 * \throw std::out_of_range when count > N
842 * \return `*this`
843 */
844 FOLLY_CPP14_CONSTEXPR BasicFixedString& assign(
845 std::size_t count,
846 Char ch) noexcept(false) {
847 detail::fixedstring::checkOverflow(count, N);
848 for (std::size_t i = 0u; i < count; ++i) {
849 data_[i] = ch;
850 }
851 size_ = count;
852 data_[size_] = Char(0);
853 return *this;
854 }
855
856 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
857 * Assigns characters from an `BasicFixedString` to this object.
858 * \note Equivalent to `assign(that, 0, that.size())`
859 */
860 template <std::size_t M>
861 FOLLY_CPP14_CONSTEXPR BasicFixedString& assign(
862 const BasicFixedString<Char, M>& that) noexcept(M <= N) {
863 return *this = that;
864 }
865
866 // Why is this overload deleted? So users aren't confused by the difference
867 // between str.assign("foo", N) and str.assign("foo"_fs, N). In the former,
868 // N is a count of characters. In the latter, it would be a position, which
869 // totally changes the meaning of the code.
870 template <std::size_t M>
871 FOLLY_CPP14_CONSTEXPR BasicFixedString& assign(
872 const BasicFixedString<Char, M>& that,
873 std::size_t pos) noexcept(false) = delete;
874
875 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
876 * Assigns `count` characters from an `BasicFixedString` to this object,
877 * starting at position `pos` in the source object.
878 * \param that The source string.
879 * \param pos The starting position in the source string.
880 * \param count The number of characters to copy. If `npos`, `count` is taken
881 * to be `that.size()-pos`.
882 * \pre `pos <= that.size()`
883 * \pre `count <= that.size()-pos`
884 * \pre `count <= N`
885 * \post `size() == count`
886 * \post `0 == strncmp(data(), that.begin() + pos, count)`
887 * \post `at(size()) == Char(0)`
888 * \throw std::out_of_range when pos > that.size() or count > that.size()-pos
889 * or count > N.
890 * \return `*this`
891 */
892 template <std::size_t M>
893 FOLLY_CPP14_CONSTEXPR BasicFixedString& assign(
894 const BasicFixedString<Char, M>& that,
895 std::size_t pos,
896 std::size_t count) noexcept(false) {
897 detail::fixedstring::checkOverflow(pos, that.size_);
898 return assign(
899 that.data_ + pos,
900 detail::fixedstring::checkOverflowOrNpos(count, that.size_ - pos));
901 }
902
903 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
904 * Assigns characters from an `BasicFixedString` to this object.
905 * \pre `that` contains no embedded nulls.
906 * \pre `that[M-1] == Char(0)`
907 * \note Equivalent to `assign(that, M - 1)`
908 */
909 template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>
910 FOLLY_CPP14_CONSTEXPR BasicFixedString& assign(
911 const Char (&that)[M]) noexcept {
912 return assign(detail::fixedstring::checkNullTerminated(that), M - 1u);
913 }
914
915 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
916 * Assigns `count` characters from a range of characters to this object.
917 * \param that A pointer to a range of characters.
918 * \param count The number of characters to copy.
919 * \pre `that` points to at least `count` characters.
920 * \pre `count <= N`
921 * \post `size() == count`
922 * \post `0 == strncmp(data(), that, count)`
923 * \post `at(size()) == Char(0)`
924 * \throw std::out_of_range when count > N
925 * \return `*this`
926 */
927 FOLLY_CPP14_CONSTEXPR BasicFixedString& assign(
928 const Char* that,
929 std::size_t count) noexcept(false) {
930 detail::fixedstring::checkOverflow(count, N);
931 for (std::size_t i = 0u; i < count; ++i) {
932 data_[i] = that[i];
933 }
934 size_ = count;
935 data_[size_] = Char(0);
936 return *this;
937 }
938
939 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
940 * Swap the contents of this string with `that`.
941 */
942 FOLLY_CPP14_CONSTEXPR void swap(BasicFixedString& that) noexcept {
943 // less-than-or-equal here to copy the null terminator:
944 for (std::size_t i = 0u; i <= folly::constexpr_max(size_, that.size_);
945 ++i) {
946 detail::fixedstring::constexpr_swap(data_[i], that.data_[i]);
947 }
948 detail::fixedstring::constexpr_swap(size_, that.size_);
949 }
950
951 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
952 * Return a pointer to a range of `size()+1` characters, the last of which
953 * is `Char(0)`.
954 */
955 FOLLY_CPP14_CONSTEXPR Char* data() noexcept {
956 return data_;
957 }
958
959 /**
960 * \overload
961 */
962 constexpr const Char* data() const noexcept {
963 return data_;
964 }
965
966 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
967 * \return `data()`.
968 */
969 constexpr const Char* c_str() const noexcept {
970 return data_;
971 }
972
973 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
974 * \return `data()`.
975 */
976 FOLLY_CPP14_CONSTEXPR Char* begin() noexcept {
977 return data_;
978 }
979
980 /**
981 * \overload
982 */
983 constexpr const Char* begin() const noexcept {
984 return data_;
985 }
986
987 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
988 * \return `data()`.
989 */
990 constexpr const Char* cbegin() const noexcept {
991 return begin();
992 }
993
994 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
995 * \return `data() + size()`.
996 */
997 FOLLY_CPP14_CONSTEXPR Char* end() noexcept {
998 return data_ + size_;
999 }
1000
1001 /**
1002 * \overload
1003 */
1004 constexpr const Char* end() const noexcept {
1005 return data_ + size_;
1006 }
1007
1008 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1009 * \return `data() + size()`.
1010 */
1011 constexpr const Char* cend() const noexcept {
1012 return end();
1013 }
1014
1015 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1016 * Returns a reverse iterator to the first character of the reversed string.
1017 * It corresponds to the last + 1 character of the non-reversed string.
1018 */
1019 FOLLY_CPP14_CONSTEXPR reverse_iterator rbegin() noexcept {
1020 return reverse_iterator{data_ + size_};
1021 }
1022
1023 /**
1024 * \overload
1025 */
1026 constexpr const_reverse_iterator rbegin() const noexcept {
1027 return const_reverse_iterator{data_ + size_};
1028 }
1029
1030 /**
1031 * \note Equivalent to `rbegin()` on a const-qualified reference to `*this`.
1032 */
1033 constexpr const_reverse_iterator crbegin() const noexcept {
1034 return rbegin();
1035 }
1036
1037 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1038 * Returns a reverse iterator to the last + 1 character of the reversed
1039 * string. It corresponds to the first character of the non-reversed string.
1040 */
1041 FOLLY_CPP14_CONSTEXPR reverse_iterator rend() noexcept {
1042 return reverse_iterator{data_};
1043 }
1044
1045 /**
1046 * \overload
1047 */
1048 constexpr const_reverse_iterator rend() const noexcept {
1049 return const_reverse_iterator{data_};
1050 }
1051
1052 /**
1053 * \note Equivalent to `rend()` on a const-qualified reference to `*this`.
1054 */
1055 constexpr const_reverse_iterator crend() const noexcept {
1056 return rend();
1057 }
1058
1059 /**
1060 * \return The number of `Char` elements in the string.
1061 */
1062 constexpr std::size_t size() const noexcept {
1063 return size_;
1064 }
1065
1066 /**
1067 * \return The number of `Char` elements in the string.
1068 */
1069 constexpr std::size_t length() const noexcept {
1070 return size_;
1071 }
1072
1073 /**
1074 * \return True if and only if `size() == 0`.
1075 */
1076 constexpr bool empty() const noexcept {
1077 return 0u == size_;
1078 }
1079
1080 /**
1081 * \return `N`.
1082 */
1083 static constexpr std::size_t capacity() noexcept {
1084 return N;
1085 }
1086
1087 /**
1088 * \return `N`.
1089 */
1090 static constexpr std::size_t max_size() noexcept {
1091 return N;
1092 }
1093
1094 // We would need to reimplement folly::Hash to make this
1095 // constexpr. :-(
1096 std::uint32_t hash() const noexcept {
1097 return folly::hsieh_hash32_buf(data_, size_);
1098 }
1099
1100 /**
1101 * \note `at(size())` is allowed will return `Char(0)`.
1102 * \return `*(data() + i)`
1103 * \throw std::out_of_range when i > size()
1104 */
1105 FOLLY_CPP14_CONSTEXPR Char& at(std::size_t i) noexcept(false) {
1106 return i <= size_ ? data_[i]
1107 : (throw_exception<std::out_of_range>(
1108 "Out of range in BasicFixedString::at"),
1109 data_[size_]);
1110 }
1111
1112 /**
1113 * \overload
1114 */
1115 constexpr const Char& at(std::size_t i) const noexcept(false) {
1116 return i <= size_ ? data_[i]
1117 : (throw_exception<std::out_of_range>(
1118 "Out of range in BasicFixedString::at"),
1119 data_[size_]);
1120 }
1121
1122 /**
1123 * \pre `i <= size()`
1124 * \note `(*this)[size()]` is allowed will return `Char(0)`.
1125 * \return `*(data() + i)`
1126 */
1127 FOLLY_CPP14_CONSTEXPR Char& operator[](std::size_t i) noexcept {
1128#ifdef NDEBUG
1129 return data_[i];
1130#else
1131 return data_[detail::fixedstring::checkOverflow(i, size_)];
1132#endif
1133 }
1134
1135 /**
1136 * \overload
1137 */
1138 constexpr const Char& operator[](std::size_t i) const noexcept {
1139#ifdef NDEBUG
1140 return data_[i];
1141#else
1142 return data_[detail::fixedstring::checkOverflow(i, size_)];
1143#endif
1144 }
1145
1146 /**
1147 * \note Equivalent to `(*this)[0]`
1148 */
1149 FOLLY_CPP14_CONSTEXPR Char& front() noexcept {
1150 return (*this)[0u];
1151 }
1152
1153 /**
1154 * \overload
1155 */
1156 constexpr const Char& front() const noexcept {
1157 return (*this)[0u];
1158 }
1159
1160 /**
1161 * \note Equivalent to `at(size()-1)`
1162 * \pre `!empty()`
1163 */
1164 FOLLY_CPP14_CONSTEXPR Char& back() noexcept {
1165#ifdef NDEBUG
1166 return data_[size_ - 1u];
1167#else
1168 return data_[size_ - detail::fixedstring::checkOverflow(1u, size_)];
1169#endif
1170 }
1171
1172 /**
1173 * \overload
1174 */
1175 constexpr const Char& back() const noexcept {
1176#ifdef NDEBUG
1177 return data_[size_ - 1u];
1178#else
1179 return data_[size_ - detail::fixedstring::checkOverflow(1u, size_)];
1180#endif
1181 }
1182
1183 /**
1184 * Clears the contents of this string.
1185 * \post `size() == 0u`
1186 * \post `at(size()) == Char(0)`
1187 */
1188 FOLLY_CPP14_CONSTEXPR void clear() noexcept {
1189 data_[0u] = Char(0);
1190 size_ = 0u;
1191 }
1192
1193 /**
1194 * \note Equivalent to `append(1u, ch)`.
1195 */
1196 FOLLY_CPP14_CONSTEXPR void push_back(Char ch) noexcept(false) {
1197 detail::fixedstring::checkOverflow(1u, N - size_);
1198 data_[size_] = ch;
1199 data_[++size_] = Char(0);
1200 }
1201
1202 /**
1203 * \note Equivalent to `cappend(1u, ch)`.
1204 */
1205 constexpr BasicFixedString<Char, N + 1u> cpush_back(Char ch) const noexcept {
1206 return cappend(ch);
1207 }
1208
1209 /**
1210 * Removes the last character from the string.
1211 * \pre `!empty()`
1212 * \post `size()` is one fewer than before calling `pop_back()`.
1213 * \post `at(size()) == Char(0)`
1214 * \post The characters in the half-open range `[0,size()-1)` are unmodified.
1215 * \throw std::out_of_range if empty().
1216 */
1217 FOLLY_CPP14_CONSTEXPR void pop_back() noexcept(false) {
1218 detail::fixedstring::checkOverflow(1u, size_);
1219 --size_;
1220 data_[size_] = Char(0);
1221 }
1222
1223 /**
1224 * Returns a new string with the first `size()-1` characters from this string.
1225 * \pre `!empty()`
1226 * \note Equivalent to `BasicFixedString<Char, N-1u>{*this, 0u, size()-1u}`
1227 * \throw std::out_of_range if empty().
1228 */
1229 constexpr BasicFixedString<Char, N - 1u> cpop_back() const noexcept(false) {
1230 return {*this, 0u, size_ - detail::fixedstring::checkOverflow(1u, size_)};
1231 }
1232
1233 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1234 * Appends `count` copies of `ch` to this string.
1235 * \pre `count + old_size <= N`
1236 * \post The first `old_size` characters of the string are unmodified.
1237 * \post `size() == old_size + count`
1238 * \throw std::out_of_range if count > N - size().
1239 */
1240 FOLLY_CPP14_CONSTEXPR BasicFixedString& append(
1241 std::size_t count,
1242 Char ch) noexcept(false) {
1243 detail::fixedstring::checkOverflow(count, N - size_);
1244 for (std::size_t i = 0u; i < count; ++i) {
1245 data_[size_ + i] = ch;
1246 }
1247 size_ += count;
1248 data_[size_] = Char(0);
1249 return *this;
1250 }
1251
1252 /**
1253 * \note Equivalent to `append(*this, 0, that.size())`.
1254 */
1255 template <std::size_t M>
1256 FOLLY_CPP14_CONSTEXPR BasicFixedString& append(
1257 const BasicFixedString<Char, M>& that) noexcept(false) {
1258 return append(that, 0u, that.size_);
1259 }
1260
1261 // Why is this overload deleted? So as not to get confused with
1262 // append("null-terminated", N), where N would be a count instead
1263 // of a position.
1264 template <std::size_t M>
1265 FOLLY_CPP14_CONSTEXPR BasicFixedString& append(
1266 const BasicFixedString<Char, M>& that,
1267 std::size_t pos) noexcept(false) = delete;
1268
1269 /**
1270 * Appends `count` characters from another string to this one, starting at a
1271 * given offset, `pos`.
1272 * \param that The source string.
1273 * \param pos The starting position in the source string.
1274 * \param count The number of characters to append. If `npos`, `count` is
1275 * taken to be `that.size()-pos`.
1276 * \pre `pos <= that.size()`
1277 * \pre `count <= that.size() - pos`
1278 * \pre `old_size + count <= N`
1279 * \post The first `old_size` characters of the string are unmodified.
1280 * \post `size() == old_size + count`
1281 * \post `at(size()) == Char(0)`
1282 * \throw std::out_of_range if pos + count > that.size() or if
1283 * `old_size + count > N`.
1284 */
1285 template <std::size_t M>
1286 FOLLY_CPP14_CONSTEXPR BasicFixedString& append(
1287 const BasicFixedString<Char, M>& that,
1288 std::size_t pos,
1289 std::size_t count) noexcept(false) {
1290 detail::fixedstring::checkOverflow(pos, that.size_);
1291 count = detail::fixedstring::checkOverflowOrNpos(count, that.size_ - pos);
1292 detail::fixedstring::checkOverflow(count, N - size_);
1293 for (std::size_t i = 0u; i < count; ++i) {
1294 data_[size_ + i] = that.data_[pos + i];
1295 }
1296 size_ += count;
1297 data_[size_] = Char(0);
1298 return *this;
1299 }
1300
1301 /**
1302 * \note Equivalent to `append(that, strlen(that))`.
1303 */
1304 FOLLY_CPP14_CONSTEXPR BasicFixedString& append(const Char* that) noexcept(
1305 false) {
1306 return append(that, folly::constexpr_strlen(that));
1307 }
1308
1309 /**
1310 * Appends `count` characters from the specified character array.
1311 * \pre `that` points to a range of at least `count` characters.
1312 * \pre `count + old_size <= N`
1313 * \post The first `old_size` characters of the string are unmodified.
1314 * \post `size() == old_size + count`
1315 * \post `at(size()) == Char(0)`
1316 * \throw std::out_of_range if old_size + count > N.
1317 */
1318 FOLLY_CPP14_CONSTEXPR BasicFixedString& append(
1319 const Char* that,
1320 std::size_t count) noexcept(false) {
1321 detail::fixedstring::checkOverflow(count, N - size_);
1322 for (std::size_t i = 0u; i < count; ++i) {
1323 data_[size_ + i] = that[i];
1324 }
1325 size_ += count;
1326 data_[size_] = Char(0);
1327 return *this;
1328 }
1329
1330 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1331 * Creates a new string by appending a character to an existing string, which
1332 * is left unmodified.
1333 * \note Equivalent to `*this + ch`
1334 */
1335 constexpr BasicFixedString<Char, N + 1u> cappend(Char ch) const noexcept {
1336 return *this + ch;
1337 }
1338
1339 /**
1340 * Creates a new string by appending a string to an existing string, which
1341 * is left unmodified.
1342 * \note Equivalent to `*this + ch`
1343 */
1344 template <std::size_t M>
1345 constexpr BasicFixedString<Char, N + M> cappend(
1346 const BasicFixedString<Char, M>& that) const noexcept {
1347 return *this + that;
1348 }
1349
1350 // Deleted to avoid confusion with append("char*", N), where N is a count
1351 // instead of a position.
1352 template <std::size_t M>
1353 constexpr BasicFixedString<Char, N + M> cappend(
1354 const BasicFixedString<Char, M>& that,
1355 std::size_t pos) const noexcept(false) = delete;
1356
1357 /**
1358 * Creates a new string by appending characters from one string to another,
1359 * which is left unmodified.
1360 * \note Equivalent to `*this + that.substr(pos, count)`
1361 */
1362 template <std::size_t M>
1363 constexpr BasicFixedString<Char, N + M> cappend(
1364 const BasicFixedString<Char, M>& that,
1365 std::size_t pos,
1366 std::size_t count) const noexcept(false) {
1367 return creplace(size_, 0u, that, pos, count);
1368 }
1369
1370 /**
1371 * Creates a new string by appending a string literal to a string,
1372 * which is left unmodified.
1373 * \note Equivalent to `*this + that`
1374 */
1375 template <std::size_t M>
1376 constexpr BasicFixedString<Char, N + M - 1u> cappend(
1377 const Char (&that)[M]) const noexcept {
1378 return creplace(size_, 0u, that);
1379 }
1380
1381 // Deleted to avoid confusion with append("char*", N), where N is a count
1382 // instead of a position
1383 template <std::size_t M>
1384 constexpr BasicFixedString<Char, N + M - 1u> cappend(
1385 const Char (&that)[M],
1386 std::size_t pos) const noexcept(false) = delete;
1387
1388 /**
1389 * Creates a new string by appending characters from one string to another,
1390 * which is left unmodified.
1391 * \note Equivalent to `*this + makeFixedString(that).substr(pos, count)`
1392 */
1393 template <std::size_t M>
1394 constexpr BasicFixedString<Char, N + M - 1u>
1395 cappend(const Char (&that)[M], std::size_t pos, std::size_t count) const
1396 noexcept(false) {
1397 return creplace(size_, 0u, that, pos, count);
1398 }
1399
1400 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1401 * Appends characters from a null-terminated string literal to this string.
1402 * \note Equivalent to `append(that)`.
1403 */
1404 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator+=(const Char* that) noexcept(
1405 false) {
1406 return append(that);
1407 }
1408
1409 /**
1410 * Appends characters from another string to this one.
1411 * \note Equivalent to `append(that)`.
1412 */
1413 template <std::size_t M>
1414 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator+=(
1415 const BasicFixedString<Char, M>& that) noexcept(false) {
1416 return append(that, 0u, that.size_);
1417 }
1418
1419 /**
1420 * Appends a character to this string.
1421 * \note Equivalent to `push_back(ch)`.
1422 */
1423 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator+=(Char ch) noexcept(false) {
1424 push_back(ch);
1425 return *this;
1426 }
1427
1428 /**
1429 * Appends characters from an `initializer_list` to this string.
1430 * \note Equivalent to `append(il.begin(), il.size())`.
1431 */
1432 FOLLY_CPP14_CONSTEXPR BasicFixedString& operator+=(
1433 std::initializer_list<Char> il) noexcept(false) {
1434 return append(il.begin(), il.size());
1435 }
1436
1437 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1438 * Erase all characters from this string.
1439 * \note Equivalent to `clear()`
1440 * \return *this;
1441 */
1442 FOLLY_CPP14_CONSTEXPR BasicFixedString& erase() noexcept {
1443 clear();
1444 return *this;
1445 }
1446
1447 /**
1448 * Erases `count` characters from position `pos`. If `count` is `npos`,
1449 * erases from `pos` to the end of the string.
1450 * \pre `pos <= size()`
1451 * \pre `count <= size() - pos || count == npos`
1452 * \post `size() == old_size - min(count, old_size - pos)`
1453 * \post `at(size()) == Char(0)`
1454 * \return *this;
1455 * \throw std::out_of_range when pos > size().
1456 */
1457 FOLLY_CPP14_CONSTEXPR BasicFixedString& erase(
1458 std::size_t pos,
1459 std::size_t count = npos) noexcept(false) {
1460 using A = const Char[1];
1461 constexpr A a{Char(0)};
1462 return replace(
1463 pos,
1464 detail::fixedstring::checkOverflowOrNpos(
1465 count, size_ - detail::fixedstring::checkOverflow(pos, size_)),
1466 a,
1467 0u);
1468 }
1469
1470 /**
1471 * \note Equivalent to `erase(first - data(), 1)`
1472 * \return A pointer to the first character after the erased character.
1473 */
1474 FOLLY_CPP14_CONSTEXPR Char* erase(const Char* first) noexcept(false) {
1475 erase(first - data_, 1u);
1476 return data_ + (first - data_);
1477 }
1478
1479 /**
1480 * \note Equivalent to `erase(first - data(), last - first)`
1481 * \return A pointer to the first character after the erased characters.
1482 */
1483 FOLLY_CPP14_CONSTEXPR Char* erase(
1484 const Char* first,
1485 const Char* last) noexcept(false) {
1486 erase(first - data_, last - first);
1487 return data_ + (first - data_);
1488 }
1489
1490 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1491 * Create a new string by erasing all the characters from this string.
1492 * \note Equivalent to `BasicFixedString<Char, 0>{}`
1493 */
1494 constexpr BasicFixedString<Char, 0u> cerase() const noexcept {
1495 return {};
1496 }
1497
1498 /**
1499 * Create a new string by erasing all the characters after position `pos` from
1500 * this string.
1501 * \note Equivalent to `creplace(pos, min(count, pos - size()), "")`
1502 */
1503 constexpr BasicFixedString cerase(std::size_t pos, std::size_t count = npos)
1504 const noexcept(false) {
1505 using A = const Char[1];
1506 return creplace(
1507 pos,
1508 detail::fixedstring::checkOverflowOrNpos(
1509 count, size_ - detail::fixedstring::checkOverflow(pos, size_)),
1510 A{Char(0)});
1511 }
1512
1513 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1514 * Compare two strings for lexicographical ordering.
1515 * \note Equivalent to
1516 * `compare(0, size(), that.data(), that.size())`
1517 */
1518 template <std::size_t M>
1519 constexpr int compare(const BasicFixedString<Char, M>& that) const noexcept {
1520 return compare(0u, size_, that, 0u, that.size_);
1521 }
1522
1523 /**
1524 * Compare two strings for lexicographical ordering.
1525 * \note Equivalent to
1526 * `compare(this_pos, this_count, that.data(), that.size())`
1527 */
1528 template <std::size_t M>
1529 constexpr int compare(
1530 std::size_t this_pos,
1531 std::size_t this_count,
1532 const BasicFixedString<Char, M>& that) const noexcept(false) {
1533 return compare(this_pos, this_count, that, 0u, that.size_);
1534 }
1535
1536 /**
1537 * Compare two strings for lexicographical ordering.
1538 * \note Equivalent to
1539 * `compare(this_pos, this_count, that.data() + that_pos, that_count)`
1540 */
1541 template <std::size_t M>
1542 constexpr int compare(
1543 std::size_t this_pos,
1544 std::size_t this_count,
1545 const BasicFixedString<Char, M>& that,
1546 std::size_t that_pos,
1547 std::size_t that_count) const noexcept(false) {
1548 return static_cast<int>(detail::fixedstring::compare_(
1549 data_,
1550 detail::fixedstring::checkOverflow(this_pos, size_),
1551 detail::fixedstring::checkOverflow(this_count, size_ - this_pos) +
1552 this_pos,
1553 that.data_,
1554 detail::fixedstring::checkOverflow(that_pos, that.size_),
1555 detail::fixedstring::checkOverflow(that_count, that.size_ - that_pos) +
1556 that_pos));
1557 }
1558
1559 /**
1560 * Compare two strings for lexicographical ordering.
1561 * \note Equivalent to `compare(0, size(), that, strlen(that))`
1562 */
1563 constexpr int compare(const Char* that) const noexcept {
1564 return compare(0u, size_, that, folly::constexpr_strlen(that));
1565 }
1566
1567 /**
1568 * \overload
1569 */
1570 constexpr int compare(Range<const Char*> that) const noexcept {
1571 return compare(0u, size_, that.begin(), that.size());
1572 }
1573
1574 /**
1575 * Compare two strings for lexicographical ordering.
1576 * \note Equivalent to
1577 * `compare(this_pos, this_count, that, strlen(that))`
1578 */
1579 constexpr int compare(
1580 std::size_t this_pos,
1581 std::size_t this_count,
1582 const Char* that) const noexcept(false) {
1583 return compare(this_pos, this_count, that, folly::constexpr_strlen(that));
1584 }
1585
1586 /**
1587 * \overload
1588 */
1589 constexpr int compare(
1590 std::size_t this_pos,
1591 std::size_t this_count,
1592 Range<const Char*> that) const noexcept(false) {
1593 return compare(this_pos, this_count, that.begin(), that.size());
1594 }
1595
1596 /**
1597 * Compare two strings for lexicographical ordering.
1598 *
1599 * Let `A` be the the
1600 * character sequence {`(*this)[this_pos]`, ...
1601 * `(*this)[this_pos + this_count - 1]`}. Let `B` be the character sequence
1602 * {`that[0]`, ...`that[count - 1]`}. Then...
1603 *
1604 * \return
1605 * - `< 0` if `A` is ordered before the `B`
1606 * - `> 0` if `B` is ordered before `A`
1607 * - `0` if `A` equals `B`.
1608 *
1609 * \throw std::out_of_range if this_pos + this_count > size().
1610 */
1611 constexpr int compare(
1612 std::size_t this_pos,
1613 std::size_t this_count,
1614 const Char* that,
1615 std::size_t that_count) const noexcept(false) {
1616 return static_cast<int>(detail::fixedstring::compare_(
1617 data_,
1618 detail::fixedstring::checkOverflow(this_pos, size_),
1619 detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos) +
1620 this_pos,
1621 that,
1622 0u,
1623 that_count));
1624 }
1625
1626 constexpr int compare(
1627 std::size_t this_pos,
1628 std::size_t this_count,
1629 Range<const Char*> that,
1630 std::size_t that_count) const noexcept(false) {
1631 return compare(
1632 this_pos,
1633 this_count,
1634 that.begin(),
1635 detail::fixedstring::checkOverflow(that_count, that.size()));
1636 }
1637
1638 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1639 * Return a substring from `pos` to the end of the string.
1640 * \note Equivalent to `BasicFixedString{*this, pos}`
1641 */
1642 constexpr BasicFixedString substr(std::size_t pos) const noexcept(false) {
1643 return {*this, pos};
1644 }
1645
1646 /**
1647 * Return a substring from `pos` to the end of the string.
1648 * \note Equivalent to `BasicFixedString{*this, pos, count}`
1649 */
1650 constexpr BasicFixedString substr(std::size_t pos, std::size_t count) const
1651 noexcept(false) {
1652 return {*this, pos, count};
1653 }
1654
1655 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1656 * Replace the characters in the range denoted by the half-open range
1657 * [`first`, `last`) with the string `that`.
1658 * \pre `first` and `last` point to characters within this string (including
1659 * the terminating null).
1660 * \note Equivalent to
1661 * `replace(first - data(), last - first, that.data(), that.size())`
1662 */
1663 template <std::size_t M>
1664 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1665 const Char* first,
1666 const Char* last,
1667 const BasicFixedString<Char, M>& that) noexcept(false) {
1668 return replace(first - data_, last - first, that, 0u, that.size_);
1669 }
1670
1671 /**
1672 * Replace `this_count` characters starting from position `this_pos` with the
1673 * characters from string `that` starting at position `that_pos`.
1674 * \pre `that_pos <= that.size()`
1675 * \note Equivalent to
1676 * <tt>replace(this_pos, this_count, that.data() + that_pos,
1677 * that.size() - that_pos)</tt>
1678 */
1679 template <std::size_t M>
1680 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1681 std::size_t this_pos,
1682 std::size_t this_count,
1683 const BasicFixedString<Char, M>& that,
1684 std::size_t that_pos = 0u) noexcept(false) {
1685 return replace(this_pos, this_count, that, that_pos, that.size_ - that_pos);
1686 }
1687
1688 /**
1689 * Replace `this_count` characters starting from position `this_pos` with
1690 * `that_count` characters from string `that` starting at position
1691 * `that_pos`.
1692 * \pre `that_pos <= that.size() && that_count <= that.size() - that_pos`
1693 * \note Equivalent to
1694 * `replace(this_pos, this_count, that.data() + that_pos, that_count)`
1695 */
1696 template <std::size_t M>
1697 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1698 std::size_t this_pos,
1699 std::size_t this_count,
1700 const BasicFixedString<Char, M>& that,
1701 std::size_t that_pos,
1702 std::size_t that_count) noexcept(false) {
1703 return *this = creplace(this_pos, this_count, that, that_pos, that_count);
1704 }
1705
1706 /**
1707 * Replace `this_count` characters starting from position `this_pos` with
1708 * the characters from the string literal `that`.
1709 * \note Equivalent to
1710 * `replace(this_pos, this_count, that, strlen(that))`
1711 */
1712 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1713 std::size_t this_pos,
1714 std::size_t this_count,
1715 const Char* that) noexcept(false) {
1716 return replace(this_pos, this_count, that, folly::constexpr_strlen(that));
1717 }
1718
1719 /**
1720 * Replace the characters denoted by the half-open range [`first`,`last`) with
1721 * the characters from the string literal `that`.
1722 * \pre `first` and `last` point to characters within this string (including
1723 * the terminating null).
1724 * \note Equivalent to
1725 * `replace(first - data(), last - first, that, strlen(that))`
1726 */
1727 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1728 const Char* first,
1729 const Char* last,
1730 const Char* that) noexcept(false) {
1731 return replace(
1732 first - data_, last - first, that, folly::constexpr_strlen(that));
1733 }
1734
1735 /**
1736 * Replace `this_count` characters starting from position `this_pos` with
1737 * `that_count` characters from the character sequence pointed to by `that`.
1738 * \param this_pos The starting offset within `*this` of the first character
1739 * to be replaced.
1740 * \param this_count The number of characters to be replaced. If `npos`,
1741 * it is treated as if `this_count` were `size() - this_pos`.
1742 * \param that A pointer to the replacement string.
1743 * \param that_count The number of characters in the replacement string.
1744 * \pre `this_pos <= size() && this_count <= size() - this_pos`
1745 * \pre `that` points to a contiguous sequence of at least `that_count`
1746 * characters
1747 * \throw std::out_of_range on any of the following conditions:
1748 * - `this_pos > size()`
1749 * - `this_count > size() - this_pos`
1750 * - `size() - this_count + that_count > N`
1751 */
1752 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1753 std::size_t this_pos,
1754 std::size_t this_count,
1755 const Char* that,
1756 std::size_t that_count) noexcept(false) {
1757 return *this = detail::fixedstring::Helper::replace_<Char>(
1758 data_,
1759 size_,
1760 detail::fixedstring::checkOverflow(this_pos, size_),
1761 detail::fixedstring::checkOverflowOrNpos(
1762 this_count, size_ - this_pos),
1763 that,
1764 0u,
1765 that_count,
1766 Indices{});
1767 }
1768
1769 /**
1770 * Replace `this_count` characters starting from position `this_pos` with
1771 * `that_count` characters `ch`.
1772 * \note Equivalent to
1773 * `replace(this_pos, this_count, BasicFixedString{that_count, ch})`
1774 */
1775 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1776 std::size_t this_pos,
1777 std::size_t this_count,
1778 std::size_t that_count,
1779 Char ch) noexcept(false) {
1780 return replace(this_pos, this_count, BasicFixedString{that_count, ch});
1781 }
1782
1783 /**
1784 * Replace the characters denoted by the half-open range [`first`,`last`)
1785 * with `that_count` characters `ch`.
1786 * \note Equivalent to
1787 * `replace(first - data(), last - first, BasicFixedString{that_count, ch})`
1788 */
1789 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1790 const Char* first,
1791 const Char* last,
1792 std::size_t that_count,
1793 Char ch) noexcept(false) {
1794 return replace(
1795 first - data_, last - first, BasicFixedString{that_count, ch});
1796 }
1797
1798 /**
1799 * Replace the characters denoted by the half-open range [`first`,`last`) with
1800 * the characters from the string literal `that`.
1801 * \pre `first` and `last` point to characters within this string (including
1802 * the terminating null).
1803 * \note Equivalent to
1804 * `replace(this_pos, this_count, il.begin(), il.size())`
1805 */
1806 FOLLY_CPP14_CONSTEXPR BasicFixedString& replace(
1807 const Char* first,
1808 const Char* last,
1809 std::initializer_list<Char> il) noexcept(false) {
1810 return replace(first - data_, last - first, il.begin(), il.size());
1811 }
1812
1813 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
1814 * Construct a new string by replacing `this_count` characters starting from
1815 * position `this_pos` within this string with the characters from string
1816 * `that` starting at position `that_pos`.
1817 * \pre `that_pos <= that.size()`
1818 * \note Equivalent to
1819 * <tt>creplace(this_pos, this_count, that, that_pos,
1820 * that.size() - that_pos)</tt>
1821 */
1822 template <std::size_t M>
1823 constexpr BasicFixedString<Char, N + M> creplace(
1824 std::size_t this_pos,
1825 std::size_t this_count,
1826 const BasicFixedString<Char, M>& that,
1827 std::size_t that_pos = 0u) const noexcept(false) {
1828 return creplace(
1829 this_pos,
1830 this_count,
1831 that,
1832 that_pos,
1833 that.size_ - detail::fixedstring::checkOverflow(that_pos, that.size_));
1834 }
1835
1836 /**
1837 * Construct a new string by replacing `this_count` characters starting from
1838 * position `this_pos` within this string with `that_count` characters from
1839 * string `that` starting at position `that_pos`.
1840 * \param this_pos The starting offset within `*this` of the first character
1841 * to be replaced.
1842 * \param this_count The number of characters to be replaced. If `npos`,
1843 * it is treated as if `this_count` were `size() - this_pos`.
1844 * \param that A string that contains the replacement string.
1845 * \param that_pos The offset to the first character in the replacement
1846 * string.
1847 * \param that_count The number of characters in the replacement string.
1848 * \pre `this_pos <= size() && this_count <= size() - this_pos`
1849 * \pre `that_pos <= that.size() && that_count <= that.size() - that_pos`
1850 * \post The size of the returned string is `size() - this_count + that_count`
1851 * \note Equivalent to <tt>BasicFixedString<Char, N + M>{substr(0, this_pos) +
1852 * that.substr(that_pos, that_count) + substr(this_pos + this_count)}</tt>
1853 * \throw std::out_of_range on any of the following conditions:
1854 * - `this_pos > size()`
1855 * - `this_count > size() - this_pos`
1856 * - `that_pos > that.size()`
1857 * - `that_count > that.size() - that_pos`
1858 */
1859 template <std::size_t M>
1860 constexpr BasicFixedString<Char, N + M> creplace(
1861 std::size_t this_pos,
1862 std::size_t this_count,
1863 const BasicFixedString<Char, M>& that,
1864 std::size_t that_pos,
1865 std::size_t that_count) const noexcept(false) {
1866 return detail::fixedstring::Helper::replace_<Char>(
1867 data_,
1868 size_,
1869 detail::fixedstring::checkOverflow(this_pos, size_),
1870 detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos),
1871 that.data_,
1872 detail::fixedstring::checkOverflow(that_pos, that.size_),
1873 detail::fixedstring::checkOverflowOrNpos(
1874 that_count, that.size_ - that_pos),
1875 folly::make_index_sequence<N + M>{});
1876 }
1877
1878 /**
1879 * Construct a new string by replacing the characters denoted by the half-open
1880 * range [`first`,`last`) within this string with the characters from string
1881 * `that` starting at position `that_pos`.
1882 * \pre `that_pos <= that.size()`
1883 * \note Equivalent to
1884 * <tt>creplace(first - data(), last - first, that, that_pos,
1885 * that.size() - that_pos)</tt>
1886 */
1887 template <std::size_t M>
1888 constexpr BasicFixedString<Char, N + M> creplace(
1889 const Char* first,
1890 const Char* last,
1891 const BasicFixedString<Char, M>& that,
1892 std::size_t that_pos = 0u) const noexcept(false) {
1893 return creplace(
1894 first - data_,
1895 last - first,
1896 that,
1897 that_pos,
1898 that.size_ - detail::fixedstring::checkOverflow(that_pos, that.size_));
1899 }
1900
1901 /**
1902 * Construct a new string by replacing the characters denoted by the half-open
1903 * range [`first`,`last`) within this string with the `that_count`
1904 * characters from string `that` starting at position `that_pos`.
1905 * \note Equivalent to
1906 * <tt>creplace(first - data(), last - first, that, that_pos,
1907 * that_count)</tt>
1908 */
1909 template <std::size_t M>
1910 constexpr BasicFixedString<Char, N + M> creplace(
1911 const Char* first,
1912 const Char* last,
1913 const BasicFixedString<Char, M>& that,
1914 std::size_t that_pos,
1915 std::size_t that_count) const noexcept(false) {
1916 return creplace(first - data_, last - first, that, that_pos, that_count);
1917 }
1918
1919 /**
1920 * Construct a new string by replacing `this_count` characters starting from
1921 * position `this_pos` within this string with `M-1` characters from
1922 * character array `that`.
1923 * \pre `strlen(that) == M-1`
1924 * \note Equivalent to
1925 * <tt>creplace(this_pos, this_count, that, 0, M - 1)</tt>
1926 */
1927 template <std::size_t M>
1928 constexpr BasicFixedString<Char, N + M - 1u> creplace(
1929 std::size_t this_pos,
1930 std::size_t this_count,
1931 const Char (&that)[M]) const noexcept(false) {
1932 return creplace(this_pos, this_count, that, 0u, M - 1u);
1933 }
1934
1935 /**
1936 * Replace `this_count` characters starting from position `this_pos` with
1937 * `that_count` characters from the character array `that` starting at
1938 * position `that_pos`.
1939 * \param this_pos The starting offset within `*this` of the first character
1940 * to be replaced.
1941 * \param this_count The number of characters to be replaced. If `npos`,
1942 * it is treated as if `this_count` were `size() - this_pos`.
1943 * \param that An array of characters containing the replacement string.
1944 * \param that_pos The starting offset of the replacement string.
1945 * \param that_count The number of characters in the replacement string. If
1946 * `npos`, it is treated as if `that_count` were `M - 1 - that_pos`
1947 * \pre `this_pos <= size() && this_count <= size() - this_pos`
1948 * \pre `that_pos <= M - 1 && that_count <= M - 1 - that_pos`
1949 * \post The size of the returned string is `size() - this_count + that_count`
1950 * \note Equivalent to <tt>BasicFixedString<Char, N + M - 1>{
1951 * substr(0, this_pos) +
1952 * makeFixedString(that).substr(that_pos, that_count) +
1953 * substr(this_pos + this_count)}</tt>
1954 * \throw std::out_of_range on any of the following conditions:
1955 * - `this_pos > size()`
1956 * - `this_count > size() - this_pos`
1957 * - `that_pos >= M`
1958 * - `that_count >= M - that_pos`
1959 */
1960 template <std::size_t M>
1961 constexpr BasicFixedString<Char, N + M - 1u> creplace(
1962 std::size_t this_pos,
1963 std::size_t this_count,
1964 const Char (&that)[M],
1965 std::size_t that_pos,
1966 std::size_t that_count) const noexcept(false) {
1967 return detail::fixedstring::Helper::replace_<Char>(
1968 data_,
1969 size_,
1970 detail::fixedstring::checkOverflow(this_pos, size_),
1971 detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos),
1972 detail::fixedstring::checkNullTerminated(that),
1973 detail::fixedstring::checkOverflow(that_pos, M - 1u),
1974 detail::fixedstring::checkOverflowOrNpos(that_count, M - 1u - that_pos),
1975 folly::make_index_sequence<N + M - 1u>{});
1976 }
1977
1978 /**
1979 * Construct a new string by replacing the characters denoted by the half-open
1980 * range [`first`,`last`) within this string with the first `M-1`
1981 * characters from the character array `that`.
1982 * \pre `strlen(that) == M-1`
1983 * \note Equivalent to
1984 * <tt>creplace(first - data(), last - first, that, 0, M-1)</tt>
1985 */
1986 template <std::size_t M>
1987 constexpr BasicFixedString<Char, N + M - 1u>
1988 creplace(const Char* first, const Char* last, const Char (&that)[M]) const
1989 noexcept(false) {
1990 return creplace(first - data_, last - first, that, 0u, M - 1u);
1991 }
1992
1993 /**
1994 * Construct a new string by replacing the characters denoted by the half-open
1995 * range [`first`,`last`) within this string with the `that_count`
1996 * characters from the character array `that` starting at position
1997 * `that_pos`.
1998 * \pre `strlen(that) == M-1`
1999 * \note Equivalent to
2000 * `creplace(first - data(), last - first, that, that_pos, that_count)`
2001 */
2002 template <std::size_t M>
2003 constexpr BasicFixedString<Char, N + M - 1u> creplace(
2004 const Char* first,
2005 const Char* last,
2006 const Char (&that)[M],
2007 std::size_t that_pos,
2008 std::size_t that_count) const noexcept(false) {
2009 return creplace(first - data_, last - first, that, that_pos, that_count);
2010 }
2011
2012 /**
2013 * Copies `min(count, size())` characters starting from offset `0`
2014 * from this string into the buffer pointed to by `dest`.
2015 * \return The number of characters copied.
2016 */
2017 FOLLY_CPP14_CONSTEXPR std::size_t copy(Char* dest, std::size_t count) const
2018 noexcept {
2019 return copy(dest, count, 0u);
2020 }
2021
2022 /**
2023 * Copies `min(count, size() - pos)` characters starting from offset `pos`
2024 * from this string into the buffer pointed to by `dest`.
2025 * \pre `pos <= size()`
2026 * \return The number of characters copied.
2027 * \throw std::out_of_range if `pos > size()`
2028 */
2029 FOLLY_CPP14_CONSTEXPR std::size_t
2030 copy(Char* dest, std::size_t count, std::size_t pos) const noexcept(false) {
2031 detail::fixedstring::checkOverflow(pos, size_);
2032 for (std::size_t i = 0u; i < count; ++i) {
2033 if (i + pos == size_) {
2034 return size_;
2035 }
2036 dest[i] = data_[i + pos];
2037 }
2038 return count;
2039 }
2040
2041 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2042 * Resizes the current string.
2043 * \note Equivalent to `resize(count, Char(0))`
2044 */
2045 FOLLY_CPP14_CONSTEXPR void resize(std::size_t count) noexcept(false) {
2046 resize(count, Char(0));
2047 }
2048
2049 /**
2050 * Resizes the current string by setting the size to `count` and setting
2051 * `data()[count]` to `Char(0)`. If `count > old_size`, the characters
2052 * in the range [`old_size`,`count`) are set to `ch`.
2053 */
2054 FOLLY_CPP14_CONSTEXPR void resize(std::size_t count, Char ch) noexcept(
2055 false) {
2056 detail::fixedstring::checkOverflow(count, N);
2057 if (count == size_) {
2058 } else if (count < size_) {
2059 size_ = count;
2060 data_[size_] = Char(0);
2061 } else {
2062 for (; size_ < count; ++size_) {
2063 data_[size_] = ch;
2064 }
2065 data_[size_] = Char(0);
2066 }
2067 }
2068
2069 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2070 * Finds the first occurrence of the character sequence `that` in this string.
2071 * \note Equivalent to `find(that.data(), 0, that.size())`
2072 */
2073 template <std::size_t M>
2074 constexpr std::size_t find(const BasicFixedString<Char, M>& that) const
2075 noexcept {
2076 return find(that, 0u);
2077 }
2078
2079 /**
2080 * Finds the first occurrence of the character sequence `that` in this string,
2081 * starting at offset `pos`.
2082 * \pre `pos <= size()`
2083 * \note Equivalent to `find(that.data(), pos, that.size())`
2084 */
2085 template <std::size_t M>
2086 constexpr std::size_t find(
2087 const BasicFixedString<Char, M>& that,
2088 std::size_t pos) const noexcept(false) {
2089 return that.size_ <= size_ - detail::fixedstring::checkOverflow(pos, size_)
2090 ? detail::fixedstring::find_(data_, size_, that.data_, pos, that.size_)
2091 : npos;
2092 }
2093
2094 /**
2095 * Finds the first occurrence of the character sequence `that` in this string.
2096 * \note Equivalent to `find(that.data(), 0, strlen(that))`
2097 */
2098 constexpr std::size_t find(const Char* that) const noexcept {
2099 return find(that, 0u, folly::constexpr_strlen(that));
2100 }
2101
2102 /**
2103 * Finds the first occurrence of the character sequence `that` in this string,
2104 * starting at offset `pos`.
2105 * \pre `pos <= size()`
2106 * \note Equivalent to `find(that.data(), pos, strlen(that))`
2107 */
2108 constexpr std::size_t find(const Char* that, std::size_t pos) const
2109 noexcept(false) {
2110 return find(that, pos, folly::constexpr_strlen(that));
2111 }
2112
2113 /**
2114 * Finds the first occurrence of the first `count` characters in the buffer
2115 * pointed to by `that` in this string, starting at offset `pos`.
2116 * \pre `pos <= size()`
2117 * \pre `that` points to a buffer containing at least `count` contiguous
2118 * characters.
2119 * \return The lowest offset `i` such that `i >= pos` and
2120 * `0 == strncmp(data() + i, that, count)`; or `npos` if there is no such
2121 * offset `i`.
2122 * \throw std::out_of_range when `pos > size()`
2123 */
2124 constexpr std::size_t find(
2125 const Char* that,
2126 std::size_t pos,
2127 std::size_t count) const noexcept(false) {
2128 return count <= size_ - detail::fixedstring::checkOverflow(pos, size_)
2129 ? detail::fixedstring::find_(data_, size_, that, pos, count)
2130 : npos;
2131 }
2132
2133 /**
2134 * Finds the first occurrence of the character `ch` in this string.
2135 * \note Equivalent to `find(&ch, 0, 1)`
2136 */
2137 constexpr std::size_t find(Char ch) const noexcept {
2138 return find(ch, 0u);
2139 }
2140
2141 /**
2142 * Finds the first occurrence of the character character `c` in this string,
2143 * starting at offset `pos`.
2144 * \pre `pos <= size()`
2145 * \note Equivalent to `find(&ch, pos, 1)`
2146 */
2147 constexpr std::size_t find(Char ch, std::size_t pos) const noexcept(false) {
2148 using A = const Char[1u];
2149 return 0u == size_ - detail::fixedstring::checkOverflow(pos, size_)
2150 ? npos
2151 : detail::fixedstring::find_(data_, size_, A{ch}, pos, 1u);
2152 }
2153
2154 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2155 * Finds the last occurrence of characters in the string
2156 * `that` in this string.
2157 * \note Equivalent to `rfind(that.data(), size(), that.size())`
2158 */
2159 template <std::size_t M>
2160 constexpr std::size_t rfind(const BasicFixedString<Char, M>& that) const
2161 noexcept {
2162 return rfind(that, size_);
2163 }
2164
2165 /**
2166 * Finds the last occurrence of characters in the string
2167 * `that` in this string, starting at offset `pos`.
2168 * \note Equivalent to `rfind(that.data(), pos, that.size())`
2169 */
2170 template <std::size_t M>
2171 constexpr std::size_t rfind(
2172 const BasicFixedString<Char, M>& that,
2173 std::size_t pos) const noexcept(false) {
2174 return that.size_ <= size_
2175 ? detail::fixedstring::rfind_(
2176 data_,
2177 that.data_,
2178 folly::constexpr_min(
2179 detail::fixedstring::checkOverflow(pos, size_),
2180 size_ - that.size_),
2181 that.size_)
2182 : npos;
2183 }
2184
2185 /**
2186 * Finds the last occurrence of characters in the buffer
2187 * pointed to by `that` in this string.
2188 * \note Equivalent to `rfind(that, size(), strlen(that))`
2189 */
2190 constexpr std::size_t rfind(const Char* that) const noexcept {
2191 return rfind(that, size_, folly::constexpr_strlen(that));
2192 }
2193
2194 /**
2195 * Finds the last occurrence of characters in the buffer
2196 * pointed to by `that` in this string, starting at offset `pos`.
2197 * \note Equivalent to `rfind(that, pos, strlen(that))`
2198 */
2199 constexpr std::size_t rfind(const Char* that, std::size_t pos) const
2200 noexcept(false) {
2201 return rfind(that, pos, folly::constexpr_strlen(that));
2202 }
2203
2204 /**
2205 * Finds the last occurrence of the first `count` characters in the buffer
2206 * pointed to by `that` in this string, starting at offset `pos`.
2207 * \pre `pos <= size()`
2208 * \pre `that` points to a buffer containing at least `count` contiguous
2209 * characters.
2210 * \return The largest offset `i` such that `i <= pos` and
2211 * `i + count <= size()` and `0 == strncmp(data() + i, that, count)`; or
2212 * `npos` if there is no such offset `i`.
2213 * \throw std::out_of_range when `pos > size()`
2214 */
2215 constexpr std::size_t rfind(
2216 const Char* that,
2217 std::size_t pos,
2218 std::size_t count) const noexcept(false) {
2219 return count <= size_
2220 ? detail::fixedstring::rfind_(
2221 data_,
2222 that,
2223 folly::constexpr_min(
2224 detail::fixedstring::checkOverflow(pos, size_),
2225 size_ - count),
2226 count)
2227 : npos;
2228 }
2229
2230 /**
2231 * Finds the last occurrence of the character character `ch` in this string.
2232 * \note Equivalent to `rfind(&ch, size(), 1)`
2233 */
2234 constexpr std::size_t rfind(Char ch) const noexcept {
2235 return rfind(ch, size_);
2236 }
2237
2238 /**
2239 * Finds the last occurrence of the character character `ch` in this string,
2240 * starting at offset `pos`.
2241 * \pre `pos <= size()`
2242 * \note Equivalent to `rfind(&ch, pos, 1)`
2243 */
2244 constexpr std::size_t rfind(Char ch, std::size_t pos) const noexcept(false) {
2245 using A = const Char[1u];
2246 return 0u == size_
2247 ? npos
2248 : detail::fixedstring::rfind_(
2249 data_,
2250 A{ch},
2251 folly::constexpr_min(
2252 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2253 1u);
2254 }
2255
2256 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2257 * Finds the first occurrence of any character in `that` in this string.
2258 * \note Equivalent to `find_first_of(that.data(), 0, that.size())`
2259 */
2260 template <std::size_t M>
2261 constexpr std::size_t find_first_of(
2262 const BasicFixedString<Char, M>& that) const noexcept {
2263 return find_first_of(that, 0u);
2264 }
2265
2266 /**
2267 * Finds the first occurrence of any character in `that` in this string,
2268 * starting at offset `pos`
2269 * \note Equivalent to `find_first_of(that.data(), pos, that.size())`
2270 */
2271 template <std::size_t M>
2272 constexpr std::size_t find_first_of(
2273 const BasicFixedString<Char, M>& that,
2274 std::size_t pos) const noexcept(false) {
2275 return size_ == detail::fixedstring::checkOverflow(pos, size_)
2276 ? npos
2277 : detail::fixedstring::find_first_of_(
2278 data_, size_, that.data_, pos, that.size_);
2279 }
2280
2281 /**
2282 * Finds the first occurrence of any character in the null-terminated
2283 * character sequence pointed to by `that` in this string.
2284 * \note Equivalent to `find_first_of(that, 0, strlen(that))`
2285 */
2286 constexpr std::size_t find_first_of(const Char* that) const noexcept {
2287 return find_first_of(that, 0u, folly::constexpr_strlen(that));
2288 }
2289
2290 /**
2291 * Finds the first occurrence of any character in the null-terminated
2292 * character sequence pointed to by `that` in this string,
2293 * starting at offset `pos`
2294 * \note Equivalent to `find_first_of(that, pos, strlen(that))`
2295 */
2296 constexpr std::size_t find_first_of(const Char* that, std::size_t pos) const
2297 noexcept(false) {
2298 return find_first_of(that, pos, folly::constexpr_strlen(that));
2299 }
2300
2301 /**
2302 * Finds the first occurrence of any character in the first `count` characters
2303 * in the buffer pointed to by `that` in this string, starting at offset
2304 * `pos`.
2305 * \pre `pos <= size()`
2306 * \pre `that` points to a buffer containing at least `count` contiguous
2307 * characters.
2308 * \return The smallest offset `i` such that `i >= pos` and
2309 * `std::find(that, that+count, at(i)) != that+count`; or
2310 * `npos` if there is no such offset `i`.
2311 * \throw std::out_of_range when `pos > size()`
2312 */
2313 constexpr std::size_t find_first_of(
2314 const Char* that,
2315 std::size_t pos,
2316 std::size_t count) const noexcept(false) {
2317 return size_ == detail::fixedstring::checkOverflow(pos, size_)
2318 ? npos
2319 : detail::fixedstring::find_first_of_(data_, size_, that, pos, count);
2320 }
2321
2322 /**
2323 * Finds the first occurrence of `ch` in this string.
2324 * \note Equivalent to `find_first_of(&ch, 0, 1)`
2325 */
2326 constexpr std::size_t find_first_of(Char ch) const noexcept {
2327 return find_first_of(ch, 0u);
2328 }
2329
2330 /**
2331 * Finds the first occurrence of `ch` in this string,
2332 * starting at offset `pos`.
2333 * \note Equivalent to `find_first_of(&ch, pos, 1)`
2334 */
2335 constexpr std::size_t find_first_of(Char ch, std::size_t pos) const
2336 noexcept(false) {
2337 using A = const Char[1u];
2338 return size_ == detail::fixedstring::checkOverflow(pos, size_)
2339 ? npos
2340 : detail::fixedstring::find_first_of_(data_, size_, A{ch}, pos, 1u);
2341 }
2342
2343 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2344 * Finds the first occurrence of any character not in `that` in this string.
2345 * \note Equivalent to `find_first_not_of(that.data(), 0, that.size())`
2346 */
2347 template <std::size_t M>
2348 constexpr std::size_t find_first_not_of(
2349 const BasicFixedString<Char, M>& that) const noexcept {
2350 return find_first_not_of(that, 0u);
2351 }
2352
2353 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2354 * Finds the first occurrence of any character not in `that` in this string.
2355 * \note Equivalent to `find_first_not_of(that.data(), 0, that.size())`
2356 */
2357 template <std::size_t M>
2358 constexpr std::size_t find_first_not_of(
2359 const BasicFixedString<Char, M>& that,
2360 std::size_t pos) const noexcept(false) {
2361 return size_ == detail::fixedstring::checkOverflow(pos, size_)
2362 ? npos
2363 : detail::fixedstring::find_first_not_of_(
2364 data_, size_, that.data_, pos, that.size_);
2365 }
2366
2367 /**
2368 * Finds the first occurrence of any character not in the null-terminated
2369 * character sequence pointed to by `that` in this string.
2370 * \note Equivalent to `find_first_not_of(that, 0, strlen(that))`
2371 */
2372 constexpr std::size_t find_first_not_of(const Char* that) const noexcept {
2373 return find_first_not_of(that, 0u, folly::constexpr_strlen(that));
2374 }
2375
2376 /**
2377 * Finds the first occurrence of any character not in the null-terminated
2378 * character sequence pointed to by `that` in this string,
2379 * starting at offset `pos`
2380 * \note Equivalent to `find_first_not_of(that, pos, strlen(that))`
2381 */
2382 constexpr std::size_t find_first_not_of(const Char* that, std::size_t pos)
2383 const noexcept(false) {
2384 return find_first_not_of(that, pos, folly::constexpr_strlen(that));
2385 }
2386
2387 /**
2388 * Finds the first occurrence of any character not in the first `count`
2389 * characters in the buffer pointed to by `that` in this string, starting at
2390 * offset `pos`.
2391 * \pre `pos <= size()`
2392 * \pre `that` points to a buffer containing at least `count` contiguous
2393 * characters.
2394 * \return The smallest offset `i` such that `i >= pos` and
2395 * `std::find(that, that+count, at(i)) == that+count`; or
2396 * `npos` if there is no such offset `i`.
2397 * \throw std::out_of_range when `pos > size()`
2398 */
2399 constexpr std::size_t find_first_not_of(
2400 const Char* that,
2401 std::size_t pos,
2402 std::size_t count) const noexcept(false) {
2403 return size_ == detail::fixedstring::checkOverflow(pos, size_)
2404 ? npos
2405 : detail::fixedstring::find_first_not_of_(
2406 data_, size_, that, pos, count);
2407 }
2408
2409 /**
2410 * Finds the first occurrence of any character other than `ch` in this string.
2411 * \note Equivalent to `find_first_not_of(&ch, 0, 1)`
2412 */
2413 constexpr std::size_t find_first_not_of(Char ch) const noexcept {
2414 return find_first_not_of(ch, 0u);
2415 }
2416
2417 /**
2418 * Finds the first occurrence of any character other than `ch` in this string,
2419 * starting at offset `pos`.
2420 * \note Equivalent to `find_first_not_of(&ch, pos, 1)`
2421 */
2422 constexpr std::size_t find_first_not_of(Char ch, std::size_t pos) const
2423 noexcept(false) {
2424 using A = const Char[1u];
2425 return 1u <= size_ - detail::fixedstring::checkOverflow(pos, size_)
2426 ? detail::fixedstring::find_first_not_of_(data_, size_, A{ch}, pos, 1u)
2427 : npos;
2428 }
2429
2430 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2431 * Finds the last occurrence of any character in `that` in this string.
2432 * \note Equivalent to `find_last_of(that.data(), size(), that.size())`
2433 */
2434 template <std::size_t M>
2435 constexpr std::size_t find_last_of(
2436 const BasicFixedString<Char, M>& that) const noexcept {
2437 return find_last_of(that, size_);
2438 }
2439
2440 /**
2441 * Finds the last occurrence of any character in `that` in this string,
2442 * starting at offset `pos`
2443 * \note Equivalent to `find_last_of(that.data(), pos, that.size())`
2444 */
2445 template <std::size_t M>
2446 constexpr std::size_t find_last_of(
2447 const BasicFixedString<Char, M>& that,
2448 std::size_t pos) const noexcept(false) {
2449 return 0u == size_
2450 ? npos
2451 : detail::fixedstring::find_last_of_(
2452 data_,
2453 that.data_,
2454 folly::constexpr_min(
2455 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2456 that.size_);
2457 }
2458
2459 /**
2460 * Finds the last occurrence of any character in the null-terminated
2461 * character sequence pointed to by `that` in this string.
2462 * \note Equivalent to `find_last_of(that, size(), strlen(that))`
2463 */
2464 constexpr std::size_t find_last_of(const Char* that) const noexcept {
2465 return find_last_of(that, size_, folly::constexpr_strlen(that));
2466 }
2467
2468 /**
2469 * Finds the last occurrence of any character in the null-terminated
2470 * character sequence pointed to by `that` in this string,
2471 * starting at offset `pos`
2472 * \note Equivalent to `find_last_of(that, pos, strlen(that))`
2473 */
2474 constexpr std::size_t find_last_of(const Char* that, std::size_t pos) const
2475 noexcept(false) {
2476 return find_last_of(that, pos, folly::constexpr_strlen(that));
2477 }
2478
2479 /**
2480 * Finds the last occurrence of any character in the first `count` characters
2481 * in the buffer pointed to by `that` in this string, starting at offset
2482 * `pos`.
2483 * \pre `pos <= size()`
2484 * \pre `that` points to a buffer containing at least `count` contiguous
2485 * characters.
2486 * \return The largest offset `i` such that `i <= pos` and
2487 * `i < size()` and `std::find(that, that+count, at(i)) != that+count`; or
2488 * `npos` if there is no such offset `i`.
2489 * \throw std::out_of_range when `pos > size()`
2490 */
2491 constexpr std::size_t find_last_of(
2492 const Char* that,
2493 std::size_t pos,
2494 std::size_t count) const noexcept(false) {
2495 return 0u == size_
2496 ? npos
2497 : detail::fixedstring::find_last_of_(
2498 data_,
2499 that,
2500 folly::constexpr_min(
2501 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2502 count);
2503 }
2504
2505 /**
2506 * Finds the last occurrence of `ch` in this string.
2507 * \note Equivalent to `find_last_of(&ch, size(), 1)`
2508 */
2509 constexpr std::size_t find_last_of(Char ch) const noexcept {
2510 return find_last_of(ch, size_);
2511 }
2512
2513 /**
2514 * Finds the last occurrence of `ch` in this string,
2515 * starting at offset `pos`.
2516 * \note Equivalent to `find_last_of(&ch, pos, 1)`
2517 */
2518 constexpr std::size_t find_last_of(Char ch, std::size_t pos) const
2519 noexcept(false) {
2520 using A = const Char[1u];
2521 return 0u == size_
2522 ? npos
2523 : detail::fixedstring::find_last_of_(
2524 data_,
2525 A{ch},
2526 folly::constexpr_min(
2527 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2528 1u);
2529 }
2530
2531 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2532 * Finds the last occurrence of any character not in `that` in this string.
2533 * \note Equivalent to `find_last_not_of(that.data(), size(), that.size())`
2534 */
2535 template <std::size_t M>
2536 constexpr std::size_t find_last_not_of(
2537 const BasicFixedString<Char, M>& that) const noexcept {
2538 return find_last_not_of(that, size_);
2539 }
2540
2541 /**
2542 * Finds the last occurrence of any character not in `that` in this string,
2543 * starting at offset `pos`
2544 * \note Equivalent to `find_last_not_of(that.data(), pos, that.size())`
2545 */
2546 template <std::size_t M>
2547 constexpr std::size_t find_last_not_of(
2548 const BasicFixedString<Char, M>& that,
2549 std::size_t pos) const noexcept(false) {
2550 return 0u == size_
2551 ? npos
2552 : detail::fixedstring::find_last_not_of_(
2553 data_,
2554 that.data_,
2555 folly::constexpr_min(
2556 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2557 that.size_);
2558 }
2559
2560 /**
2561 * Finds the last occurrence of any character not in the null-terminated
2562 * character sequence pointed to by `that` in this string.
2563 * \note Equivalent to `find_last_not_of(that, size(), strlen(that))`
2564 */
2565 constexpr std::size_t find_last_not_of(const Char* that) const noexcept {
2566 return find_last_not_of(that, size_, folly::constexpr_strlen(that));
2567 }
2568
2569 /**
2570 * Finds the last occurrence of any character not in the null-terminated
2571 * character sequence pointed to by `that` in this string,
2572 * starting at offset `pos`
2573 * \note Equivalent to `find_last_not_of(that, pos, strlen(that))`
2574 */
2575 constexpr std::size_t find_last_not_of(const Char* that, std::size_t pos)
2576 const noexcept(false) {
2577 return find_last_not_of(that, pos, folly::constexpr_strlen(that));
2578 }
2579
2580 /**
2581 * Finds the last occurrence of any character not in the first `count`
2582 * characters in the buffer pointed to by `that` in this string, starting at
2583 * offset `pos`.
2584 * \pre `pos <= size()`
2585 * \pre `that` points to a buffer containing at least `count` contiguous
2586 * characters.
2587 * \return The largest offset `i` such that `i <= pos` and
2588 * `i < size()` and `std::find(that, that+count, at(i)) == that+count`; or
2589 * `npos` if there is no such offset `i`.
2590 * \throw std::out_of_range when `pos > size()`
2591 */
2592 constexpr std::size_t find_last_not_of(
2593 const Char* that,
2594 std::size_t pos,
2595 std::size_t count) const noexcept(false) {
2596 return 0u == size_
2597 ? npos
2598 : detail::fixedstring::find_last_not_of_(
2599 data_,
2600 that,
2601 folly::constexpr_min(
2602 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2603 count);
2604 }
2605
2606 /**
2607 * Finds the last occurrence of any character other than `ch` in this string.
2608 * \note Equivalent to `find_last_not_of(&ch, size(), 1)`
2609 */
2610 constexpr std::size_t find_last_not_of(Char ch) const noexcept {
2611 return find_last_not_of(ch, size_);
2612 }
2613
2614 /**
2615 * Finds the last occurrence of any character other than `ch` in this string,
2616 * starting at offset `pos`.
2617 * \note Equivalent to `find_last_not_of(&ch, pos, 1)`
2618 */
2619 constexpr std::size_t find_last_not_of(Char ch, std::size_t pos) const
2620 noexcept(false) {
2621 using A = const Char[1u];
2622 return 0u == size_
2623 ? npos
2624 : detail::fixedstring::find_last_not_of_(
2625 data_,
2626 A{ch},
2627 folly::constexpr_min(
2628 detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),
2629 1u);
2630 }
2631
2632 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2633 * Asymmetric relational operators
2634 */
2635 friend constexpr bool operator==(
2636 const Char* a,
2637 const BasicFixedString& b) noexcept {
2638 return detail::fixedstring::equal_(
2639 a, folly::constexpr_strlen(a), b.data_, b.size_);
2640 }
2641
2642 /**
2643 * \overload
2644 */
2645 friend constexpr bool operator==(
2646 const BasicFixedString& a,
2647 const Char* b) noexcept {
2648 return b == a;
2649 }
2650
2651 /**
2652 * \overload
2653 */
2654 friend constexpr bool operator==(
2655 Range<const Char*> a,
2656 const BasicFixedString& b) noexcept {
2657 return detail::fixedstring::equal_(a.begin(), a.size(), b.data_, b.size_);
2658 }
2659
2660 /**
2661 * \overload
2662 */
2663 friend constexpr bool operator==(
2664 const BasicFixedString& a,
2665 Range<const Char*> b) noexcept {
2666 return b == a;
2667 }
2668
2669 friend constexpr bool operator!=(
2670 const Char* a,
2671 const BasicFixedString& b) noexcept {
2672 return !(a == b);
2673 }
2674
2675 /**
2676 * \overload
2677 */
2678 friend constexpr bool operator!=(
2679 const BasicFixedString& a,
2680 const Char* b) noexcept {
2681 return !(b == a);
2682 }
2683
2684 /**
2685 * \overload
2686 */
2687 friend constexpr bool operator!=(
2688 Range<const Char*> a,
2689 const BasicFixedString& b) noexcept {
2690 return !(a == b);
2691 }
2692
2693 /**
2694 * \overload
2695 */
2696 friend constexpr bool operator!=(
2697 const BasicFixedString& a,
2698 Range<const Char*> b) noexcept {
2699 return !(a == b);
2700 }
2701
2702 friend constexpr bool operator<(
2703 const Char* a,
2704 const BasicFixedString& b) noexcept {
2705 return ordering::lt ==
2706 detail::fixedstring::compare_(
2707 a, 0u, folly::constexpr_strlen(a), b.data_, 0u, b.size_);
2708 }
2709
2710 /**
2711 * \overload
2712 */
2713 friend constexpr bool operator<(
2714 const BasicFixedString& a,
2715 const Char* b) noexcept {
2716 return ordering::lt ==
2717 detail::fixedstring::compare_(
2718 a.data_, 0u, a.size_, b, 0u, folly::constexpr_strlen(b));
2719 }
2720
2721 /**
2722 * \overload
2723 */
2724 friend constexpr bool operator<(
2725 Range<const Char*> a,
2726 const BasicFixedString& b) noexcept {
2727 return ordering::lt ==
2728 detail::fixedstring::compare_(
2729 a.begin(), 0u, a.size(), b.data_, 0u, b.size_);
2730 }
2731
2732 /**
2733 * \overload
2734 */
2735 friend constexpr bool operator<(
2736 const BasicFixedString& a,
2737 Range<const Char*> b) noexcept {
2738 return ordering::lt ==
2739 detail::fixedstring::compare_(
2740 a.data_, 0u, a.size_, b.begin(), 0u, b.size());
2741 }
2742
2743 friend constexpr bool operator>(
2744 const Char* a,
2745 const BasicFixedString& b) noexcept {
2746 return b < a;
2747 }
2748
2749 /**
2750 * \overload
2751 */
2752 friend constexpr bool operator>(
2753 const BasicFixedString& a,
2754 const Char* b) noexcept {
2755 return b < a;
2756 }
2757
2758 /**
2759 * \overload
2760 */
2761 friend constexpr bool operator>(
2762 Range<const Char*> a,
2763 const BasicFixedString& b) noexcept {
2764 return b < a;
2765 }
2766
2767 /**
2768 * \overload
2769 */
2770 friend constexpr bool operator>(
2771 const BasicFixedString& a,
2772 Range<const Char*> b) noexcept {
2773 return b < a;
2774 }
2775
2776 friend constexpr bool operator<=(
2777 const Char* a,
2778 const BasicFixedString& b) noexcept {
2779 return !(b < a);
2780 }
2781
2782 /**
2783 * \overload
2784 */
2785 friend constexpr bool operator<=(
2786 const BasicFixedString& a,
2787 const Char* b) noexcept {
2788 return !(b < a);
2789 }
2790
2791 /**
2792 * \overload
2793 */
2794 friend constexpr bool operator<=(
2795 Range<const Char*> const& a,
2796 const BasicFixedString& b) noexcept {
2797 return !(b < a);
2798 }
2799
2800 /**
2801 * \overload
2802 */
2803 friend constexpr bool operator<=(
2804 const BasicFixedString& a,
2805 Range<const Char*> b) noexcept {
2806 return !(b < a);
2807 }
2808
2809 friend constexpr bool operator>=(
2810 const Char* a,
2811 const BasicFixedString& b) noexcept {
2812 return !(a < b);
2813 }
2814
2815 /**
2816 * \overload
2817 */
2818 friend constexpr bool operator>=(
2819 const BasicFixedString& a,
2820 const Char* b) noexcept {
2821 return !(a < b);
2822 }
2823
2824 /**
2825 * \overload
2826 */
2827 friend constexpr bool operator>=(
2828 Range<const Char*> a,
2829 const BasicFixedString& b) noexcept {
2830 return !(a < b);
2831 }
2832
2833 /**
2834 * \overload
2835 */
2836 friend constexpr bool operator>=(
2837 const BasicFixedString& a,
2838 Range<const Char*> const& b) noexcept {
2839 return !(a < b);
2840 }
2841
2842 /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2843 * Asymmetric concatenation
2844 */
2845 template <std::size_t M>
2846 friend constexpr BasicFixedString<Char, N + M - 1u> operator+(
2847 const Char (&a)[M],
2848 const BasicFixedString& b) noexcept {
2849 return detail::fixedstring::Helper::concat_<Char>(
2850 detail::fixedstring::checkNullTerminated(a),
2851 M - 1u,
2852 b.data_,
2853 b.size_,
2854 folly::make_index_sequence<N + M - 1u>{});
2855 }
2856
2857 /**
2858 * \overload
2859 */
2860 template <std::size_t M>
2861 friend constexpr BasicFixedString<Char, N + M - 1u> operator+(
2862 const BasicFixedString& a,
2863 const Char (&b)[M]) noexcept {
2864 return detail::fixedstring::Helper::concat_<Char>(
2865 a.data_,
2866 a.size_,
2867 detail::fixedstring::checkNullTerminated(b),
2868 M - 1u,
2869 folly::make_index_sequence<N + M - 1u>{});
2870 }
2871
2872 /**
2873 * \overload
2874 */
2875 friend constexpr BasicFixedString<Char, N + 1u> operator+(
2876 Char a,
2877 const BasicFixedString& b) noexcept {
2878 using A = const Char[2u];
2879 return detail::fixedstring::Helper::concat_<Char>(
2880 A{a, Char(0)},
2881 1u,
2882 b.data_,
2883 b.size_,
2884 folly::make_index_sequence<N + 1u>{});
2885 }
2886
2887 /**
2888 * \overload
2889 */
2890 friend constexpr BasicFixedString<Char, N + 1u> operator+(
2891 const BasicFixedString& a,
2892 Char b) noexcept {
2893 using A = const Char[2u];
2894 return detail::fixedstring::Helper::concat_<Char>(
2895 a.data_,
2896 a.size_,
2897 A{b, Char(0)},
2898 1u,
2899 folly::make_index_sequence<N + 1u>{});
2900 }
2901};
2902
2903template <class C, std::size_t N>
2904inline std::basic_ostream<C>& operator<<(
2905 std::basic_ostream<C>& os,
2906 const BasicFixedString<C, N>& string) {
2907 using StreamSize = decltype(os.width());
2908 os.write(string.begin(), static_cast<StreamSize>(string.size()));
2909 return os;
2910}
2911
2912/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2913 * Symmetric relational operators
2914 */
2915template <class Char, std::size_t A, std::size_t B>
2916constexpr bool operator==(
2917 const BasicFixedString<Char, A>& a,
2918 const BasicFixedString<Char, B>& b) noexcept {
2919 return detail::fixedstring::equal_(
2920 detail::fixedstring::Helper::data_(a),
2921 a.size(),
2922 detail::fixedstring::Helper::data_(b),
2923 b.size());
2924}
2925
2926template <class Char, std::size_t A, std::size_t B>
2927constexpr bool operator!=(
2928 const BasicFixedString<Char, A>& a,
2929 const BasicFixedString<Char, B>& b) {
2930 return !(a == b);
2931}
2932
2933template <class Char, std::size_t A, std::size_t B>
2934constexpr bool operator<(
2935 const BasicFixedString<Char, A>& a,
2936 const BasicFixedString<Char, B>& b) noexcept {
2937 return ordering::lt ==
2938 detail::fixedstring::compare_(
2939 detail::fixedstring::Helper::data_(a),
2940 0u,
2941 a.size(),
2942 detail::fixedstring::Helper::data_(b),
2943 0u,
2944 b.size());
2945}
2946
2947template <class Char, std::size_t A, std::size_t B>
2948constexpr bool operator>(
2949 const BasicFixedString<Char, A>& a,
2950 const BasicFixedString<Char, B>& b) noexcept {
2951 return b < a;
2952}
2953
2954template <class Char, std::size_t A, std::size_t B>
2955constexpr bool operator<=(
2956 const BasicFixedString<Char, A>& a,
2957 const BasicFixedString<Char, B>& b) noexcept {
2958 return !(b < a);
2959}
2960
2961template <class Char, std::size_t A, std::size_t B>
2962constexpr bool operator>=(
2963 const BasicFixedString<Char, A>& a,
2964 const BasicFixedString<Char, B>& b) noexcept {
2965 return !(a < b);
2966}
2967
2968/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2969 * Symmetric concatenation
2970 */
2971template <class Char, std::size_t N, std::size_t M>
2972constexpr BasicFixedString<Char, N + M> operator+(
2973 const BasicFixedString<Char, N>& a,
2974 const BasicFixedString<Char, M>& b) noexcept {
2975 return detail::fixedstring::Helper::concat_<Char>(
2976 detail::fixedstring::Helper::data_(a),
2977 a.size(),
2978 detail::fixedstring::Helper::data_(b),
2979 b.size(),
2980 folly::make_index_sequence<N + M>{});
2981}
2982
2983/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2984 * Construct a `BasicFixedString` object from a null-terminated array of
2985 * characters. The capacity and size of the string will be equal to one less
2986 * than the size of the array.
2987 * \pre `a` contains no embedded null characters.
2988 * \pre `a[N-1] == Char(0)`
2989 * \post For a returned string `s`, `s[i]==a[i]` for every `i` in [`0`,`N-1`].
2990 */
2991template <class Char, std::size_t N>
2992constexpr BasicFixedString<Char, N - 1u> makeFixedString(
2993 const Char (&a)[N]) noexcept {
2994 return {a};
2995}
2996
2997/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
2998 * Swap function
2999 */
3000template <class Char, std::size_t N>
3001FOLLY_CPP14_CONSTEXPR void swap(
3002 BasicFixedString<Char, N>& a,
3003 BasicFixedString<Char, N>& b) noexcept {
3004 a.swap(b);
3005}
3006
3007inline namespace literals {
3008inline namespace string_literals {
3009inline namespace {
3010// "const std::size_t&" is so that folly::npos has the same address in every
3011// translation unit. This is to avoid potential violations of the ODR.
3012constexpr const std::size_t& npos = detail::fixedstring::FixedStringBase::npos;
3013} // namespace
3014
3015#if defined(__GNUC__) && !defined(__ICC)
3016#pragma GCC diagnostic push
3017#pragma GCC diagnostic ignored "-Wpragmas"
3018#pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
3019
3020/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *
3021 * User-defined literals for creating FixedString objects from string literals
3022 * on the compilers that support it.
3023 *
3024 * \par Example:
3025 * \par
3026 * \code
3027 * using namespace folly::string_literals;
3028 * constexpr auto hello = "hello world!"_fs;
3029 * \endcode
3030 *
3031 * \note This requires a GNU compiler extension
3032 * (-Wgnu-string-literal-operator-template) supported by clang and gcc,
3033 * proposed for standardization in
3034 * <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0424r0.pdf>.
3035 * \par
3036 * For portable code, prefer the suffixes `_fs4`, `_fs8`, `_fs16`, `_fs32`,
3037 * `_fs64`, and `_fs128` for creating instances of types `FixedString<4>`,
3038 * `FixedString<8>`, `FixedString<16>`, etc.
3039 */
3040template <class Char, Char... Cs>
3041constexpr BasicFixedString<Char, sizeof...(Cs)> operator"" _fs() noexcept {
3042#if __cplusplus >= 201402L
3043 const Char a[] = {Cs..., Char(0)};
3044 return {+a, sizeof...(Cs)};
3045#else
3046 using A = const Char[sizeof...(Cs) + 1u];
3047 // The `+` in `+A{etc}` forces the array type to decay to a pointer
3048 return {+A{Cs..., Char(0)}, sizeof...(Cs)};
3049#endif
3050}
3051
3052#pragma GCC diagnostic pop
3053#endif
3054
3055#define FOLLY_DEFINE_FIXED_STRING_UDL(N) \
3056 constexpr FixedString<N> operator"" _fs##N( \
3057 const char* that, std::size_t count) noexcept(false) { \
3058 return {that, count}; \
3059 } \
3060/**/
3061
3062// Define UDLs _fs4, _fs8, _fs16, etc for FixedString<[4, 8, 16, ...]>
3063FOLLY_DEFINE_FIXED_STRING_UDL(4)
3064FOLLY_DEFINE_FIXED_STRING_UDL(8)
3065FOLLY_DEFINE_FIXED_STRING_UDL(16)
3066FOLLY_DEFINE_FIXED_STRING_UDL(32)
3067FOLLY_DEFINE_FIXED_STRING_UDL(64)
3068FOLLY_DEFINE_FIXED_STRING_UDL(128)
3069
3070#undef FOLLY_DEFINE_FIXED_STRING_UDL
3071} // namespace string_literals
3072} // namespace literals
3073
3074// TODO:
3075// // numeric conversions:
3076// template <std::size_t N>
3077// constexpr int stoi(const FixedString<N>& str, int base = 10);
3078// template <std::size_t N>
3079// constexpr unsigned stou(const FixedString<N>& str, int base = 10);
3080// template <std::size_t N>
3081// constexpr long stol(const FixedString<N>& str, int base = 10);
3082// template <std::size_t N>
3083// constexpr unsigned long stoul(const FixedString<N>& str, int base = 10;
3084// template <std::size_t N>
3085// constexpr long long stoll(const FixedString<N>& str, int base = 10);
3086// template <std::size_t N>
3087// constexpr unsigned long long stoull(const FixedString<N>& str,
3088// int base = 10);
3089// template <std::size_t N>
3090// constexpr float stof(const FixedString<N>& str);
3091// template <std::size_t N>
3092// constexpr double stod(const FixedString<N>& str);
3093// template <std::size_t N>
3094// constexpr long double stold(const FixedString<N>& str);
3095// template <int val>
3096// constexpr FixedString</*...*/> to_fixed_string_i() noexcept;
3097// template <unsigned val>
3098// constexpr FixedString</*...*/> to_fixed_string_u() noexcept;
3099// template <long val>
3100// constexpr FixedString</*...*/> to_fixed_string_l() noexcept;
3101// template <unsigned long val>
3102// constexpr FixedString</*...*/> to_fixed_string_ul() noexcept;
3103// template <long long val>
3104// constexpr FixedString</*...*/> to_fixed_string_ll() noexcept
3105// template <unsigned long long val>
3106// constexpr FixedString</*...*/> to_fixed_string_ull() noexcept;
3107} // namespace folly
3108