xref: /aosp_15_r20/external/fbjni/cxx/fbjni/detail/SimpleFixedString.h (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
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 // Simplified fork of folly::FixedString to avoid folly dependency.
18 
19 #pragma once
20 
21 #include <cassert>
22 #include <cstring>
23 #include <stdexcept>
24 #include <string>
25 #include <utility>
26 
27 #if defined(__has_feature)
28 #define FBJNI_HAS_FEATURE(...) __has_feature(__VA_ARGS__)
29 #else
30 #define FBJNI_HAS_FEATURE(...) 0
31 #endif
32 
33 namespace facebook {
34 namespace jni {
35 
36 namespace detail {
37 
38 template <std::size_t N>
39 class SimpleFixedString;
40 
constexpr_strlen_internal(const char * s,size_t len)41 constexpr size_t constexpr_strlen_internal(const char* s, size_t len) {
42   // clang-format off
43   return
44       *(s + 0) == char(0) ? len + 0 :
45       *(s + 1) == char(0) ? len + 1 :
46       *(s + 2) == char(0) ? len + 2 :
47       *(s + 3) == char(0) ? len + 3 :
48       *(s + 4) == char(0) ? len + 4 :
49       *(s + 5) == char(0) ? len + 5 :
50       *(s + 6) == char(0) ? len + 6 :
51       *(s + 7) == char(0) ? len + 7 :
52       constexpr_strlen_internal(s + 8, len + 8);
53   // clang-format on
54 }
55 static_assert(
56     constexpr_strlen_internal("123456789", 0) == 9,
57     "Someone appears to have broken constexpr_strlen...");
58 
constexpr_strlen(const char * s)59 constexpr size_t constexpr_strlen(const char* s) {
60 #if FBJNI_HAS_FEATURE(cxx_constexpr_string_builtins)
61   // clang provides a constexpr builtin
62   return __builtin_strlen(s);
63 #elif defined(__GNUC__) && !defined(__clang__)
64   // strlen() happens to already be constexpr under gcc
65   return std::strlen(s);
66 #else
67   return detail::constexpr_strlen_internal(s, 0);
68 #endif
69 }
70 
71 namespace fixedstring {
72 
73 // Intentionally NOT constexpr. By making this not constexpr, we make
74 // checkOverflow below ill-formed in a constexpr context when the condition
75 // it's testing for fails. In this way, precondition violations are reported
76 // at compile-time instead of at runtime.
assertOutOfBounds()77 [[noreturn]] inline void assertOutOfBounds() {
78   assert(!(bool)"Array index out of bounds in SimpleFixedString");
79   throw std::out_of_range("Array index out of bounds in SimpleFixedString");
80 }
81 
checkOverflow(std::size_t i,std::size_t max)82 constexpr std::size_t checkOverflow(std::size_t i, std::size_t max) {
83   return i <= max ? i : (void(assertOutOfBounds()), max);
84 }
85 
checkOverflowOrNpos(std::size_t i,std::size_t max)86 constexpr std::size_t checkOverflowOrNpos(std::size_t i, std::size_t max) {
87   return i == static_cast<std::size_t>(-1)
88       ? max
89       : (i <= max ? i : (void(assertOutOfBounds()), max));
90 }
91 
92 // Intentionally NOT constexpr. See note above for assertOutOfBounds
assertNotNullTerminated()93 [[noreturn]] inline void assertNotNullTerminated() noexcept {
94   assert(!(
95       bool)"Non-null terminated string used to initialize a SimpleFixedString");
96   std::terminate(); // Fail hard, fail fast.
97 }
98 
99 // Parsing help for human readers: the following is a constexpr noexcept
100 // function that accepts a reference to an array as a parameter and returns
101 // a reference to the same array.
102 template <std::size_t N>
checkNullTerminated(const char (& a)[N])103 constexpr const char (&checkNullTerminated(const char (&a)[N]) noexcept)[N] {
104   // Strange decltype(a)(a) used to make MSVC happy.
105   if (a[N - 1u] == '\0'
106 #ifndef NDEBUG
107       // In Debug mode, guard against embedded nulls:
108       && N - 1u == detail::constexpr_strlen_internal(a, 0u)
109 #endif
110   ) {
111     return decltype(a)(a);
112   } else {
113     assertNotNullTerminated();
114     return decltype(a)(a);
115   }
116 }
117 
118 struct Helper {
119   template <class Left, class Right, std::size_t... Is>
concat_Helper120   static constexpr SimpleFixedString<sizeof...(Is)> concat_(
121       const Left& left,
122       std::size_t left_count,
123       const Right& right,
124       std::size_t right_count,
125       std::index_sequence<Is...> is) noexcept {
126     return {left, left_count, right, right_count, is};
127   }
128 
129   template <std::size_t N>
130   static constexpr const char (
data_Helper131       &data_(const SimpleFixedString<N>& that) noexcept)[N + 1u] {
132     return that.data_;
133   }
134 };
135 
136 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)137 constexpr int compare_(
138     const Left& left,
139     std::size_t left_pos,
140     std::size_t left_size,
141     const Right& right,
142     std::size_t right_pos,
143     std::size_t right_size) noexcept {
144   return left_pos == left_size
145       ? (right_pos == right_size ? 0 : -1)
146       : (right_pos == right_size ? 1
147                                  : (left[left_pos] < right[right_pos]
148                                         ? -1
149                                         : (left[left_pos] > right[right_pos]
150                                                ? 1
151                                                : fixedstring::compare_(
152                                                      left,
153                                                      left_pos + 1u,
154                                                      left_size,
155                                                      right,
156                                                      right_pos + 1u,
157                                                      right_size))));
158 }
159 
160 template <class Left, class Right>
equal_(const Left & left,std::size_t left_size,const Right & right,std::size_t right_size)161 constexpr bool equal_(
162     const Left& left,
163     std::size_t left_size,
164     const Right& right,
165     std::size_t right_size) noexcept {
166   return left_size == right_size &&
167       0 == compare_(left, 0u, left_size, right, 0u, right_size);
168 }
169 
170 template <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)171 constexpr char char_at_(
172     const Left& left,
173     std::size_t left_count,
174     const Right& right,
175     std::size_t right_count,
176     std::size_t i) noexcept {
177   return i < left_count                ? left[i]
178       : i < (left_count + right_count) ? right[i - left_count]
179                                        : '\0';
180 }
181 
182 } // namespace fixedstring
183 
184 template <std::size_t N>
185 class SimpleFixedString {
186  private:
187   template <std::size_t>
188   friend class SimpleFixedString;
189   friend struct detail::fixedstring::Helper;
190 
191   char data_[N + 1u]; // +1 for the null terminator
192   std::size_t size_; // Nbr of chars, not incl. null teminator. size_ <= N
193 
194   using Indices = std::make_index_sequence<N>;
195 
196   template <class That, std::size_t... Is>
197   constexpr SimpleFixedString(
198       const That& that,
199       std::size_t size,
200       std::index_sequence<Is...>,
201       std::size_t pos = 0,
202       std::size_t count = static_cast<std::size_t>(-1)) noexcept
203       : data_{(Is < (size - pos) && Is < count ? that[Is + pos] : '\0')..., '\0'},
204         size_{(size - pos) < count ? (size - pos) : count} {}
205 
206   // Concatenation constructor
207   template <class Left, class Right, std::size_t... Is>
SimpleFixedString(const Left & left,std::size_t left_size,const Right & right,std::size_t right_size,std::index_sequence<Is...>)208   constexpr SimpleFixedString(
209       const Left& left,
210       std::size_t left_size,
211       const Right& right,
212       std::size_t right_size,
213       std::index_sequence<Is...>) noexcept
214       : data_{detail::fixedstring::char_at_(left, left_size, right, right_size, Is)..., '\0'},
215         size_{left_size + right_size} {}
216 
217  public:
SimpleFixedString()218   constexpr SimpleFixedString() : data_{}, size_{} {}
219 
220   constexpr SimpleFixedString(const SimpleFixedString&) = default;
221 
222   template <std::size_t M>
SimpleFixedString(const SimpleFixedString<M> & that)223   constexpr SimpleFixedString(const SimpleFixedString<M>& that) noexcept(M <= N)
224       : SimpleFixedString{that, 0u, that.size_} {}
225 
226   // Support substr.
227   template <std::size_t M>
SimpleFixedString(const SimpleFixedString<M> & that,std::size_t pos,std::size_t count)228   constexpr SimpleFixedString(
229       const SimpleFixedString<M>& that,
230       std::size_t pos,
231       std::size_t count) noexcept(false)
232       : SimpleFixedString{
233             that.data_,
234             that.size_,
235             std::make_index_sequence<(M < N ? M : N)>{},
236             pos,
237             detail::fixedstring::checkOverflow(
238                 detail::fixedstring::checkOverflowOrNpos(
239                     count,
240                     that.size_ -
241                         detail::fixedstring::checkOverflow(pos, that.size_)),
242                 N)} {}
243 
244   // Construct from literal.
245   template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>
SimpleFixedString(const char (& literal)[M])246   constexpr SimpleFixedString(const char (&literal)[M]) noexcept
247       : SimpleFixedString{
248             detail::fixedstring::checkNullTerminated(literal),
249             M - 1u,
250             std::make_index_sequence<M - 1u>{}} {}
251 
SimpleFixedString(const char * str,std::size_t count)252   constexpr SimpleFixedString(const char* str, std::size_t count) noexcept(
253       false)
254       : SimpleFixedString{
255             str,
256             detail::fixedstring::checkOverflow(count, N),
257             Indices{}} {}
258 
c_str()259   constexpr const char* c_str() const noexcept {
260     return data_;
261   }
262 
size()263   constexpr std::size_t size() const noexcept {
264     return size_;
265   }
266 
substr(std::size_t pos,std::size_t count)267   constexpr SimpleFixedString substr(std::size_t pos, std::size_t count) const {
268     return {*this, pos, count};
269   }
270 
271   /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
272    * Asymmetric relational operators
273    */
274   friend constexpr bool operator==(
275       const char* a,
276       const SimpleFixedString& b) noexcept {
277     return detail::fixedstring::equal_(
278         a, constexpr_strlen(a), b.data_, b.size_);
279   }
280 
281   /**
282    * \overload
283    */
284   friend constexpr bool operator==(
285       const SimpleFixedString& a,
286       const char* b) noexcept {
287     return b == a;
288   }
289 
290   /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
291    * Asymmetric concatenation
292    */
293   template <std::size_t M>
294   friend constexpr SimpleFixedString<N + M - 1u> operator+(
295       const char (&a)[M],
296       const SimpleFixedString& b) noexcept {
297     return detail::fixedstring::Helper::concat_(
298         detail::fixedstring::checkNullTerminated(a),
299         M - 1u,
300         b.data_,
301         b.size_,
302         std::make_index_sequence<N + M - 1u>{});
303   }
304 
305   template <std::size_t M>
306   friend constexpr SimpleFixedString<N + M - 1u> operator+(
307       const SimpleFixedString& a,
308       const char (&b)[M]) noexcept {
309     return detail::fixedstring::Helper::concat_(
310         a.data_,
311         a.size_,
312         detail::fixedstring::checkNullTerminated(b),
313         M - 1u,
314         std::make_index_sequence<N + M - 1u>{});
315   }
316 
begin()317   constexpr const char* begin() const noexcept {
318     return data_;
319   }
320 
end()321   constexpr const char* end() const noexcept {
322     return data_ + size_;
323   }
324 
string()325   operator std::string() const noexcept(false) {
326     return std::string(begin(), end());
327   }
328 };
329 
330 template <std::size_t N, std::size_t M>
331 constexpr SimpleFixedString<N + M> operator+(
332     const SimpleFixedString<N>& a,
333     const SimpleFixedString<M>& b) noexcept {
334   return detail::fixedstring::Helper::concat_(
335       detail::fixedstring::Helper::data_(a),
336       a.size(),
337       detail::fixedstring::Helper::data_(b),
338       b.size(),
339       std::make_index_sequence<N + M>{});
340 }
341 
342 template <std::size_t N>
makeSimpleFixedString(const char (& a)[N])343 constexpr SimpleFixedString<N - 1u> makeSimpleFixedString(
344     const char (&a)[N]) noexcept {
345   return {a};
346 }
347 
348 } // namespace detail
349 } // namespace jni
350 } // namespace facebook
351