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