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