1 /* auto-generated on 2024-01-29 13:13:24 -0500. Do not edit! */
2 /* begin file include/ada.h */
3 /**
4 * @file ada.h
5 * @brief Includes all definitions for Ada.
6 */
7 #ifndef ADA_H
8 #define ADA_H
9
10 /* begin file include/ada/ada_idna.h */
11 /* auto-generated on 2023-09-19 15:58:51 -0400. Do not edit! */
12 /* begin file include/idna.h */
13 #ifndef ADA_IDNA_H
14 #define ADA_IDNA_H
15
16 /* begin file include/ada/idna/unicode_transcoding.h */
17 #ifndef ADA_IDNA_UNICODE_TRANSCODING_H
18 #define ADA_IDNA_UNICODE_TRANSCODING_H
19
20 #include <string>
21 #include <string_view>
22
23 namespace ada::idna {
24
25 size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output);
26
27 size_t utf8_length_from_utf32(const char32_t* buf, size_t len);
28
29 size_t utf32_length_from_utf8(const char* buf, size_t len);
30
31 size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output);
32
33 } // namespace ada::idna
34
35 #endif // ADA_IDNA_UNICODE_TRANSCODING_H
36 /* end file include/ada/idna/unicode_transcoding.h */
37 /* begin file include/ada/idna/mapping.h */
38 #ifndef ADA_IDNA_MAPPING_H
39 #define ADA_IDNA_MAPPING_H
40
41 #include <string>
42 #include <string_view>
43
44 namespace ada::idna {
45
46 // If the input is ascii, then the mapping is just -> lower case.
47 void ascii_map(char* input, size_t length);
48 // check whether an ascii string needs mapping
49 bool ascii_has_upper_case(char* input, size_t length);
50 // Map the characters according to IDNA, returning the empty string on error.
51 std::u32string map(std::u32string_view input);
52
53 } // namespace ada::idna
54
55 #endif
56 /* end file include/ada/idna/mapping.h */
57 /* begin file include/ada/idna/normalization.h */
58 #ifndef ADA_IDNA_NORMALIZATION_H
59 #define ADA_IDNA_NORMALIZATION_H
60
61 #include <string>
62 #include <string_view>
63
64 namespace ada::idna {
65
66 // Normalize the characters according to IDNA (Unicode Normalization Form C).
67 void normalize(std::u32string& input);
68
69 } // namespace ada::idna
70 #endif
71 /* end file include/ada/idna/normalization.h */
72 /* begin file include/ada/idna/punycode.h */
73 #ifndef ADA_IDNA_PUNYCODE_H
74 #define ADA_IDNA_PUNYCODE_H
75
76 #include <string>
77 #include <string_view>
78
79 namespace ada::idna {
80
81 bool punycode_to_utf32(std::string_view input, std::u32string& out);
82 bool verify_punycode(std::string_view input);
83 bool utf32_to_punycode(std::u32string_view input, std::string& out);
84
85 } // namespace ada::idna
86
87 #endif // ADA_IDNA_PUNYCODE_H
88 /* end file include/ada/idna/punycode.h */
89 /* begin file include/ada/idna/validity.h */
90 #ifndef ADA_IDNA_VALIDITY_H
91 #define ADA_IDNA_VALIDITY_H
92
93 #include <string>
94 #include <string_view>
95
96 namespace ada::idna {
97
98 /**
99 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
100 */
101 bool is_label_valid(std::u32string_view label);
102
103 } // namespace ada::idna
104
105 #endif // ADA_IDNA_VALIDITY_H
106 /* end file include/ada/idna/validity.h */
107 /* begin file include/ada/idna/to_ascii.h */
108 #ifndef ADA_IDNA_TO_ASCII_H
109 #define ADA_IDNA_TO_ASCII_H
110
111 #include <string>
112 #include <string_view>
113
114 namespace ada::idna {
115
116 // Converts a domain (e.g., www.google.com) possibly containing international
117 // characters to an ascii domain (with punycode). It will not do percent
118 // decoding: percent decoding should be done prior to calling this function. We
119 // do not remove tabs and spaces, they should have been removed prior to calling
120 // this function. We also do not trim control characters. We also assume that
121 // the input is not empty. We return "" on error.
122 //
123 //
124 // This function may accept or even produce invalid domains.
125 std::string to_ascii(std::string_view ut8_string);
126
127 // Returns true if the string contains a forbidden code point according to the
128 // WHATGL URL specification:
129 // https://url.spec.whatwg.org/#forbidden-domain-code-point
130 bool contains_forbidden_domain_code_point(std::string_view ascii_string);
131
132 bool begins_with(std::u32string_view view, std::u32string_view prefix);
133 bool begins_with(std::string_view view, std::string_view prefix);
134
135 bool constexpr is_ascii(std::u32string_view view);
136 bool constexpr is_ascii(std::string_view view);
137
138 } // namespace ada::idna
139
140 #endif // ADA_IDNA_TO_ASCII_H
141 /* end file include/ada/idna/to_ascii.h */
142 /* begin file include/ada/idna/to_unicode.h */
143
144 #ifndef ADA_IDNA_TO_UNICODE_H
145 #define ADA_IDNA_TO_UNICODE_H
146
147 #include <string_view>
148
149 namespace ada::idna {
150
151 std::string to_unicode(std::string_view input);
152
153 } // namespace ada::idna
154
155 #endif // ADA_IDNA_TO_UNICODE_H
156 /* end file include/ada/idna/to_unicode.h */
157
158 #endif
159 /* end file include/idna.h */
160 /* end file include/ada/ada_idna.h */
161 /* begin file include/ada/character_sets-inl.h */
162 /**
163 * @file character_sets-inl.h
164 * @brief Definitions of the character sets used by unicode functions.
165 * @author Node.js
166 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
167 */
168 #ifndef ADA_CHARACTER_SETS_INL_H
169 #define ADA_CHARACTER_SETS_INL_H
170
171 /* begin file include/ada/character_sets.h */
172 /**
173 * @file character_sets.h
174 * @brief Declaration of the character sets used by unicode functions.
175 * @author Node.js
176 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
177 */
178 #ifndef ADA_CHARACTER_SETS_H
179 #define ADA_CHARACTER_SETS_H
180
181 /* begin file include/ada/common_defs.h */
182 /**
183 * @file common_defs.h
184 * @brief Common definitions for cross-platform compiler support.
185 */
186 #ifndef ADA_COMMON_DEFS_H
187 #define ADA_COMMON_DEFS_H
188
189 #ifdef _MSC_VER
190 #define ADA_VISUAL_STUDIO 1
191 /**
192 * We want to differentiate carefully between
193 * clang under visual studio and regular visual
194 * studio.
195 */
196 #ifdef __clang__
197 // clang under visual studio
198 #define ADA_CLANG_VISUAL_STUDIO 1
199 #else
200 // just regular visual studio (best guess)
201 #define ADA_REGULAR_VISUAL_STUDIO 1
202 #endif // __clang__
203 #endif // _MSC_VER
204
205 #if defined(__GNUC__)
206 // Marks a block with a name so that MCA analysis can see it.
207 #define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
208 #define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
209 #define ADA_DEBUG_BLOCK(name, block) \
210 BEGIN_DEBUG_BLOCK(name); \
211 block; \
212 END_DEBUG_BLOCK(name);
213 #else
214 #define ADA_BEGIN_DEBUG_BLOCK(name)
215 #define ADA_END_DEBUG_BLOCK(name)
216 #define ADA_DEBUG_BLOCK(name, block)
217 #endif
218
219 // Align to N-byte boundary
220 #define ADA_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
221 #define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n)-1))
222
223 #define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
224
225 #if defined(ADA_REGULAR_VISUAL_STUDIO)
226
227 #define ada_really_inline __forceinline
228 #define ada_never_inline __declspec(noinline)
229
230 #define ada_unused
231 #define ada_warn_unused
232
233 #ifndef ada_likely
234 #define ada_likely(x) x
235 #endif
236 #ifndef ada_unlikely
237 #define ada_unlikely(x) x
238 #endif
239
240 #define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
241 #define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
242 #define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
243 __pragma(warning(disable : WARNING_NUMBER))
244 // Get rid of Intellisense-only warnings (Code Analysis)
245 // Though __has_include is C++17, it is supported in Visual Studio 2017 or
246 // better (_MSC_VER>=1910).
247 #ifdef __has_include
248 #if __has_include(<CppCoreCheck\Warnings.h>)
249 #include <CppCoreCheck\Warnings.h>
250 #define ADA_DISABLE_UNDESIRED_WARNINGS \
251 ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
252 #endif
253 #endif
254
255 #ifndef ADA_DISABLE_UNDESIRED_WARNINGS
256 #define ADA_DISABLE_UNDESIRED_WARNINGS
257 #endif
258
259 #define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
260 #define ADA_DISABLE_STRICT_OVERFLOW_WARNING
261 #define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
262
263 #else // ADA_REGULAR_VISUAL_STUDIO
264
265 #define ada_really_inline inline __attribute__((always_inline))
266 #define ada_never_inline inline __attribute__((noinline))
267
268 #define ada_unused __attribute__((unused))
269 #define ada_warn_unused __attribute__((warn_unused_result))
270
271 #ifndef ada_likely
272 #define ada_likely(x) __builtin_expect(!!(x), 1)
273 #endif
274 #ifndef ada_unlikely
275 #define ada_unlikely(x) __builtin_expect(!!(x), 0)
276 #endif
277
278 #define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
279 // gcc doesn't seem to disable all warnings with all and extra, add warnings
280 // here as necessary
281 #define ADA_PUSH_DISABLE_ALL_WARNINGS \
282 ADA_PUSH_DISABLE_WARNINGS \
283 ADA_DISABLE_GCC_WARNING("-Weffc++") \
284 ADA_DISABLE_GCC_WARNING("-Wall") \
285 ADA_DISABLE_GCC_WARNING("-Wconversion") \
286 ADA_DISABLE_GCC_WARNING("-Wextra") \
287 ADA_DISABLE_GCC_WARNING("-Wattributes") \
288 ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
289 ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor") \
290 ADA_DISABLE_GCC_WARNING("-Wreturn-type") \
291 ADA_DISABLE_GCC_WARNING("-Wshadow") \
292 ADA_DISABLE_GCC_WARNING("-Wunused-parameter") \
293 ADA_DISABLE_GCC_WARNING("-Wunused-variable")
294 #define ADA_PRAGMA(P) _Pragma(#P)
295 #define ADA_DISABLE_GCC_WARNING(WARNING) \
296 ADA_PRAGMA(GCC diagnostic ignored WARNING)
297 #if defined(ADA_CLANG_VISUAL_STUDIO)
298 #define ADA_DISABLE_UNDESIRED_WARNINGS \
299 ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
300 #else
301 #define ADA_DISABLE_UNDESIRED_WARNINGS
302 #endif
303 #define ADA_DISABLE_DEPRECATED_WARNING \
304 ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
305 #define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
306 ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
307 #define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
308
309 #endif // MSC_VER
310
311 #if defined(ADA_VISUAL_STUDIO)
312 /**
313 * It does not matter here whether you are using
314 * the regular visual studio or clang under visual
315 * studio.
316 */
317 #if ADA_USING_LIBRARY
318 #define ADA_DLLIMPORTEXPORT __declspec(dllimport)
319 #else
320 #define ADA_DLLIMPORTEXPORT __declspec(dllexport)
321 #endif
322 #else
323 #define ADA_DLLIMPORTEXPORT
324 #endif
325
326 /// If EXPR is an error, returns it.
327 #define ADA_TRY(EXPR) \
328 { \
329 auto _err = (EXPR); \
330 if (_err) { \
331 return _err; \
332 } \
333 }
334
335 // __has_cpp_attribute is part of C++20
336 #if !defined(__has_cpp_attribute)
337 #define __has_cpp_attribute(x) 0
338 #endif
339
340 #if __has_cpp_attribute(gnu::noinline)
341 #define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
342 #else
343 #define ADA_ATTRIBUTE_NOINLINE
344 #endif
345
346 namespace ada {
unreachable()347 [[noreturn]] inline void unreachable() {
348 #ifdef __GNUC__
349 __builtin_unreachable();
350 #elif defined(_MSC_VER)
351 __assume(false);
352 #else
353 #endif
354 }
355 } // namespace ada
356
357 #if defined(__GNUC__) && !defined(__clang__)
358 #if __GNUC__ <= 8
359 #define ADA_OLD_GCC 1
360 #endif // __GNUC__ <= 8
361 #endif // defined(__GNUC__) && !defined(__clang__)
362
363 #if ADA_OLD_GCC
364 #define ada_constexpr
365 #else
366 #define ada_constexpr constexpr
367 #endif
368
369 #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
370 #define ADA_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
371 #elif defined(_WIN32)
372 #define ADA_IS_BIG_ENDIAN 0
373 #else
374 #if defined(__APPLE__) || \
375 defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined
376 // __ORDER_BIG_ENDIAN__
377 #include <machine/endian.h>
378 #elif defined(sun) || \
379 defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__)
380 #include <sys/byteorder.h>
381 #else // defined(__APPLE__) || defined(__FreeBSD__)
382
383 #ifdef __has_include
384 #if __has_include(<endian.h>)
385 #include <endian.h>
386 #endif //__has_include(<endian.h>)
387 #endif //__has_include
388
389 #endif // defined(__APPLE__) || defined(__FreeBSD__)
390
391 #ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__)
392 #define ADA_IS_BIG_ENDIAN 0
393 #endif
394
395 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
396 #define ADA_IS_BIG_ENDIAN 0
397 #else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
398 #define ADA_IS_BIG_ENDIAN 1
399 #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
400
401 #endif // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
402
403 // Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
404 // we want to set it under debug builds. We detect a debug build
405 // under Visual Studio when the _DEBUG macro is set. Under the other
406 // compilers, we use the fact that they define __OPTIMIZE__ whenever
407 // they allow optimizations.
408 // It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
409 // is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
410 // It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
411 // sets _DEBUG in a release build under Visual Studio, or if some compiler fails
412 // to set the __OPTIMIZE__ macro).
413 #if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
414 #ifdef _MSC_VER
415 // Visual Studio seems to set _DEBUG for debug builds.
416 #ifdef _DEBUG
417 #define ADA_DEVELOPMENT_CHECKS 1
418 #endif // _DEBUG
419 #else // _MSC_VER
420 // All other compilers appear to set __OPTIMIZE__ to a positive integer
421 // when the compiler is optimizing.
422 #ifndef __OPTIMIZE__
423 #define ADA_DEVELOPMENT_CHECKS 1
424 #endif // __OPTIMIZE__
425 #endif // _MSC_VER
426 #endif // ADA_DEVELOPMENT_CHECKS
427
428 #define ADA_STR(x) #x
429
430 #if ADA_DEVELOPMENT_CHECKS
431 #define ADA_REQUIRE(EXPR) \
432 { \
433 if (!(EXPR) { abort(); }) }
434
435 #define ADA_FAIL(MESSAGE) \
436 do { \
437 std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
438 abort(); \
439 } while (0);
440 #define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE) \
441 do { \
442 if (LHS != RHS) { \
443 std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
444 ADA_FAIL(MESSAGE); \
445 } \
446 } while (0);
447 #define ADA_ASSERT_TRUE(COND) \
448 do { \
449 if (!(COND)) { \
450 std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
451 << std::endl; \
452 ADA_FAIL(ADA_STR(COND)); \
453 } \
454 } while (0);
455 #else
456 #define ADA_FAIL(MESSAGE)
457 #define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
458 #define ADA_ASSERT_TRUE(COND)
459 #endif
460
461 #ifdef ADA_VISUAL_STUDIO
462 #define ADA_ASSUME(COND) __assume(COND)
463 #else
464 #define ADA_ASSUME(COND) \
465 do { \
466 if (!(COND)) __builtin_unreachable(); \
467 } while (0)
468 #endif
469
470 #if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
471 (defined(_M_AMD64) || defined(_M_X64) || \
472 (defined(_M_IX86_FP) && _M_IX86_FP == 2))
473 #define ADA_SSE2 1
474 #endif
475
476 #if defined(__aarch64__) || defined(_M_ARM64)
477 #define ADA_NEON 1
478 #endif
479
480 #endif // ADA_COMMON_DEFS_H
481 /* end file include/ada/common_defs.h */
482 #include <cstdint>
483
484 /**
485 * @namespace ada::character_sets
486 * @brief Includes the definitions for unicode character sets.
487 */
488 namespace ada::character_sets {
489 ada_really_inline bool bit_at(const uint8_t a[], uint8_t i);
490 } // namespace ada::character_sets
491
492 #endif // ADA_CHARACTER_SETS_H
493 /* end file include/ada/character_sets.h */
494
495 namespace ada::character_sets {
496
497 constexpr char hex[1024] =
498 "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
499 "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
500 "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
501 "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
502 "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
503 "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
504 "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
505 "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
506 "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
507 "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
508 "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
509 "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
510 "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
511 "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
512 "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
513 "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
514 "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
515 "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
516 "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
517 "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
518 "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
519 "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
520 "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
521 "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
522 "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
523 "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
524 "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
525 "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
526 "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
527 "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
528 "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
529 "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
530
531 constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
532 // 00 01 02 03 04 05 06 07
533 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
534 // 08 09 0A 0B 0C 0D 0E 0F
535 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
536 // 10 11 12 13 14 15 16 17
537 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
538 // 18 19 1A 1B 1C 1D 1E 1F
539 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
540 // 20 21 22 23 24 25 26 27
541 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
542 // 28 29 2A 2B 2C 2D 2E 2F
543 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
544 // 30 31 32 33 34 35 36 37
545 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
546 // 38 39 3A 3B 3C 3D 3E 3F
547 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
548 // 40 41 42 43 44 45 46 47
549 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
550 // 48 49 4A 4B 4C 4D 4E 4F
551 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
552 // 50 51 52 53 54 55 56 57
553 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
554 // 58 59 5A 5B 5C 5D 5E 5F
555 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
556 // 60 61 62 63 64 65 66 67
557 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
558 // 68 69 6A 6B 6C 6D 6E 6F
559 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
560 // 70 71 72 73 74 75 76 77
561 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
562 // 78 79 7A 7B 7C 7D 7E 7F
563 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
564 // 80 81 82 83 84 85 86 87
565 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
566 // 88 89 8A 8B 8C 8D 8E 8F
567 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
568 // 90 91 92 93 94 95 96 97
569 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
570 // 98 99 9A 9B 9C 9D 9E 9F
571 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
572 // A0 A1 A2 A3 A4 A5 A6 A7
573 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
574 // A8 A9 AA AB AC AD AE AF
575 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
576 // B0 B1 B2 B3 B4 B5 B6 B7
577 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
578 // B8 B9 BA BB BC BD BE BF
579 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
580 // C0 C1 C2 C3 C4 C5 C6 C7
581 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
582 // C8 C9 CA CB CC CD CE CF
583 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
584 // D0 D1 D2 D3 D4 D5 D6 D7
585 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
586 // D8 D9 DA DB DC DD DE DF
587 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
588 // E0 E1 E2 E3 E4 E5 E6 E7
589 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
590 // E8 E9 EA EB EC ED EE EF
591 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
592 // F0 F1 F2 F3 F4 F5 F6 F7
593 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
594 // F8 F9 FA FB FC FD FE FF
595 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
596
597 constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
598 // 00 01 02 03 04 05 06 07
599 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
600 // 08 09 0A 0B 0C 0D 0E 0F
601 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
602 // 10 11 12 13 14 15 16 17
603 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
604 // 18 19 1A 1B 1C 1D 1E 1F
605 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
606 // 20 21 22 23 24 25 26 27
607 0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
608 // 28 29 2A 2B 2C 2D 2E 2F
609 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
610 // 30 31 32 33 34 35 36 37
611 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
612 // 38 39 3A 3B 3C 3D 3E 3F
613 0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
614 // 40 41 42 43 44 45 46 47
615 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
616 // 48 49 4A 4B 4C 4D 4E 4F
617 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
618 // 50 51 52 53 54 55 56 57
619 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
620 // 58 59 5A 5B 5C 5D 5E 5F
621 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
622 // 60 61 62 63 64 65 66 67
623 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
624 // 68 69 6A 6B 6C 6D 6E 6F
625 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
626 // 70 71 72 73 74 75 76 77
627 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
628 // 78 79 7A 7B 7C 7D 7E 7F
629 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
630 // 80 81 82 83 84 85 86 87
631 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
632 // 88 89 8A 8B 8C 8D 8E 8F
633 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
634 // 90 91 92 93 94 95 96 97
635 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
636 // 98 99 9A 9B 9C 9D 9E 9F
637 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
638 // A0 A1 A2 A3 A4 A5 A6 A7
639 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
640 // A8 A9 AA AB AC AD AE AF
641 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
642 // B0 B1 B2 B3 B4 B5 B6 B7
643 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
644 // B8 B9 BA BB BC BD BE BF
645 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
646 // C0 C1 C2 C3 C4 C5 C6 C7
647 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
648 // C8 C9 CA CB CC CD CE CF
649 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
650 // D0 D1 D2 D3 D4 D5 D6 D7
651 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
652 // D8 D9 DA DB DC DD DE DF
653 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
654 // E0 E1 E2 E3 E4 E5 E6 E7
655 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
656 // E8 E9 EA EB EC ED EE EF
657 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
658 // F0 F1 F2 F3 F4 F5 F6 F7
659 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
660 // F8 F9 FA FB FC FD FE FF
661 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
662
663 constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
664 // 00 01 02 03 04 05 06 07
665 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
666 // 08 09 0A 0B 0C 0D 0E 0F
667 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
668 // 10 11 12 13 14 15 16 17
669 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
670 // 18 19 1A 1B 1C 1D 1E 1F
671 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
672 // 20 21 22 23 24 25 26 27
673 0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
674 // 28 29 2A 2B 2C 2D 2E 2F
675 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
676 // 30 31 32 33 34 35 36 37
677 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
678 // 38 39 3A 3B 3C 3D 3E 3F
679 0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
680 // 40 41 42 43 44 45 46 47
681 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
682 // 48 49 4A 4B 4C 4D 4E 4F
683 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
684 // 50 51 52 53 54 55 56 57
685 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
686 // 58 59 5A 5B 5C 5D 5E 5F
687 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
688 // 60 61 62 63 64 65 66 67
689 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
690 // 68 69 6A 6B 6C 6D 6E 6F
691 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
692 // 70 71 72 73 74 75 76 77
693 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
694 // 78 79 7A 7B 7C 7D 7E 7F
695 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
696 // 80 81 82 83 84 85 86 87
697 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
698 // 88 89 8A 8B 8C 8D 8E 8F
699 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
700 // 90 91 92 93 94 95 96 97
701 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
702 // 98 99 9A 9B 9C 9D 9E 9F
703 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
704 // A0 A1 A2 A3 A4 A5 A6 A7
705 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
706 // A8 A9 AA AB AC AD AE AF
707 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
708 // B0 B1 B2 B3 B4 B5 B6 B7
709 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
710 // B8 B9 BA BB BC BD BE BF
711 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
712 // C0 C1 C2 C3 C4 C5 C6 C7
713 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
714 // C8 C9 CA CB CC CD CE CF
715 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
716 // D0 D1 D2 D3 D4 D5 D6 D7
717 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
718 // D8 D9 DA DB DC DD DE DF
719 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
720 // E0 E1 E2 E3 E4 E5 E6 E7
721 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
722 // E8 E9 EA EB EC ED EE EF
723 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
724 // F0 F1 F2 F3 F4 F5 F6 F7
725 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
726 // F8 F9 FA FB FC FD FE FF
727 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
728
729 constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
730 // 00 01 02 03 04 05 06 07
731 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
732 // 08 09 0A 0B 0C 0D 0E 0F
733 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
734 // 10 11 12 13 14 15 16 17
735 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
736 // 18 19 1A 1B 1C 1D 1E 1F
737 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
738 // 20 21 22 23 24 25 26 27
739 0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
740 // 28 29 2A 2B 2C 2D 2E 2F
741 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
742 // 30 31 32 33 34 35 36 37
743 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
744 // 38 39 3A 3B 3C 3D 3E 3F
745 0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
746 // 40 41 42 43 44 45 46 47
747 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
748 // 48 49 4A 4B 4C 4D 4E 4F
749 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
750 // 50 51 52 53 54 55 56 57
751 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
752 // 58 59 5A 5B 5C 5D 5E 5F
753 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
754 // 60 61 62 63 64 65 66 67
755 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
756 // 68 69 6A 6B 6C 6D 6E 6F
757 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
758 // 70 71 72 73 74 75 76 77
759 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
760 // 78 79 7A 7B 7C 7D 7E 7F
761 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
762 // 80 81 82 83 84 85 86 87
763 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
764 // 88 89 8A 8B 8C 8D 8E 8F
765 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
766 // 90 91 92 93 94 95 96 97
767 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
768 // 98 99 9A 9B 9C 9D 9E 9F
769 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
770 // A0 A1 A2 A3 A4 A5 A6 A7
771 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
772 // A8 A9 AA AB AC AD AE AF
773 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
774 // B0 B1 B2 B3 B4 B5 B6 B7
775 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
776 // B8 B9 BA BB BC BD BE BF
777 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
778 // C0 C1 C2 C3 C4 C5 C6 C7
779 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
780 // C8 C9 CA CB CC CD CE CF
781 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
782 // D0 D1 D2 D3 D4 D5 D6 D7
783 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
784 // D8 D9 DA DB DC DD DE DF
785 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
786 // E0 E1 E2 E3 E4 E5 E6 E7
787 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
788 // E8 E9 EA EB EC ED EE EF
789 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
790 // F0 F1 F2 F3 F4 F5 F6 F7
791 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
792 // F8 F9 FA FB FC FD FE FF
793 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
794
795 constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
796 // 00 01 02 03 04 05 06 07
797 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
798 // 08 09 0A 0B 0C 0D 0E 0F
799 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
800 // 10 11 12 13 14 15 16 17
801 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
802 // 18 19 1A 1B 1C 1D 1E 1F
803 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
804 // 20 21 22 23 24 25 26 27
805 0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
806 // 28 29 2A 2B 2C 2D 2E 2F
807 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
808 // 30 31 32 33 34 35 36 37
809 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
810 // 38 39 3A 3B 3C 3D 3E 3F
811 0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
812 // 40 41 42 43 44 45 46 47
813 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
814 // 48 49 4A 4B 4C 4D 4E 4F
815 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
816 // 50 51 52 53 54 55 56 57
817 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
818 // 58 59 5A 5B 5C 5D 5E 5F
819 0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
820 // 60 61 62 63 64 65 66 67
821 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
822 // 68 69 6A 6B 6C 6D 6E 6F
823 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
824 // 70 71 72 73 74 75 76 77
825 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
826 // 78 79 7A 7B 7C 7D 7E 7F
827 0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
828 // 80 81 82 83 84 85 86 87
829 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
830 // 88 89 8A 8B 8C 8D 8E 8F
831 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
832 // 90 91 92 93 94 95 96 97
833 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
834 // 98 99 9A 9B 9C 9D 9E 9F
835 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
836 // A0 A1 A2 A3 A4 A5 A6 A7
837 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
838 // A8 A9 AA AB AC AD AE AF
839 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
840 // B0 B1 B2 B3 B4 B5 B6 B7
841 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
842 // B8 B9 BA BB BC BD BE BF
843 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
844 // C0 C1 C2 C3 C4 C5 C6 C7
845 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
846 // C8 C9 CA CB CC CD CE CF
847 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
848 // D0 D1 D2 D3 D4 D5 D6 D7
849 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
850 // D8 D9 DA DB DC DD DE DF
851 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
852 // E0 E1 E2 E3 E4 E5 E6 E7
853 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
854 // E8 E9 EA EB EC ED EE EF
855 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
856 // F0 F1 F2 F3 F4 F5 F6 F7
857 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
858 // F8 F9 FA FB FC FD FE FF
859 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
860
861 constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
862 // 00 01 02 03 04 05 06 07
863 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
864 // 08 09 0A 0B 0C 0D 0E 0F
865 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
866 // 10 11 12 13 14 15 16 17
867 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
868 // 18 19 1A 1B 1C 1D 1E 1F
869 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
870 // 20 21 22 23 24 25 26 27
871 0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
872 // 28 29 2A 2B 2C 2D 2E 2F
873 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
874 // 30 31 32 33 34 35 36 37
875 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
876 // 38 39 3A 3B 3C 3D 3E 3F
877 0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
878 // 40 41 42 43 44 45 46 47
879 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
880 // 48 49 4A 4B 4C 4D 4E 4F
881 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
882 // 50 51 52 53 54 55 56 57
883 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
884 // 58 59 5A 5B 5C 5D 5E 5F
885 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
886 // 60 61 62 63 64 65 66 67
887 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
888 // 68 69 6A 6B 6C 6D 6E 6F
889 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
890 // 70 71 72 73 74 75 76 77
891 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
892 // 78 79 7A 7B 7C 7D 7E 7F
893 0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
894 // 80 81 82 83 84 85 86 87
895 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
896 // 88 89 8A 8B 8C 8D 8E 8F
897 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
898 // 90 91 92 93 94 95 96 97
899 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
900 // 98 99 9A 9B 9C 9D 9E 9F
901 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
902 // A0 A1 A2 A3 A4 A5 A6 A7
903 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
904 // A8 A9 AA AB AC AD AE AF
905 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
906 // B0 B1 B2 B3 B4 B5 B6 B7
907 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
908 // B8 B9 BA BB BC BD BE BF
909 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
910 // C0 C1 C2 C3 C4 C5 C6 C7
911 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
912 // C8 C9 CA CB CC CD CE CF
913 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
914 // D0 D1 D2 D3 D4 D5 D6 D7
915 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
916 // D8 D9 DA DB DC DD DE DF
917 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
918 // E0 E1 E2 E3 E4 E5 E6 E7
919 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
920 // E8 E9 EA EB EC ED EE EF
921 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
922 // F0 F1 F2 F3 F4 F5 F6 F7
923 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
924 // F8 F9 FA FB FC FD FE FF
925 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
926
927 constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
928 // 00 01 02 03 04 05 06 07
929 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
930 // 08 09 0A 0B 0C 0D 0E 0F
931 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
932 // 10 11 12 13 14 15 16 17
933 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
934 // 18 19 1A 1B 1C 1D 1E 1F
935 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
936 // 20 21 22 23 24 25 26 27
937 0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
938 // 28 29 2A 2B 2C 2D 2E 2F
939 0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x00,
940 // 30 31 32 33 34 35 36 37
941 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
942 // 38 39 3A 3B 3C 3D 3E 3F
943 0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
944 // 40 41 42 43 44 45 46 47
945 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
946 // 48 49 4A 4B 4C 4D 4E 4F
947 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
948 // 50 51 52 53 54 55 56 57
949 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
950 // 58 59 5A 5B 5C 5D 5E 5F
951 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
952 // 60 61 62 63 64 65 66 67
953 0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
954 // 68 69 6A 6B 6C 6D 6E 6F
955 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
956 // 70 71 72 73 74 75 76 77
957 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
958 // 78 79 7A 7B 7C 7D 7E 7F
959 0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x40 | 0x80,
960 // 80 81 82 83 84 85 86 87
961 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
962 // 88 89 8A 8B 8C 8D 8E 8F
963 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
964 // 90 91 92 93 94 95 96 97
965 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
966 // 98 99 9A 9B 9C 9D 9E 9F
967 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
968 // A0 A1 A2 A3 A4 A5 A6 A7
969 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
970 // A8 A9 AA AB AC AD AE AF
971 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
972 // B0 B1 B2 B3 B4 B5 B6 B7
973 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
974 // B8 B9 BA BB BC BD BE BF
975 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
976 // C0 C1 C2 C3 C4 C5 C6 C7
977 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
978 // C8 C9 CA CB CC CD CE CF
979 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
980 // D0 D1 D2 D3 D4 D5 D6 D7
981 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
982 // D8 D9 DA DB DC DD DE DF
983 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
984 // E0 E1 E2 E3 E4 E5 E6 E7
985 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
986 // E8 E9 EA EB EC ED EE EF
987 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
988 // F0 F1 F2 F3 F4 F5 F6 F7
989 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
990 // F8 F9 FA FB FC FD FE FF
991 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
992
bit_at(const uint8_t a[],const uint8_t i)993 ada_really_inline bool bit_at(const uint8_t a[], const uint8_t i) {
994 return !!(a[i >> 3] & (1 << (i & 7)));
995 }
996
997 } // namespace ada::character_sets
998
999 #endif // ADA_CHARACTER_SETS_INL_H
1000 /* end file include/ada/character_sets-inl.h */
1001 /* begin file include/ada/checkers-inl.h */
1002 /**
1003 * @file checkers-inl.h
1004 * @brief Definitions for URL specific checkers used within Ada.
1005 */
1006 #ifndef ADA_CHECKERS_INL_H
1007 #define ADA_CHECKERS_INL_H
1008
1009
1010 #include <algorithm>
1011 #include <string_view>
1012 #include <cstring>
1013
1014 namespace ada::checkers {
1015
has_hex_prefix_unsafe(std::string_view input)1016 inline bool has_hex_prefix_unsafe(std::string_view input) {
1017 // This is actually efficient code, see has_hex_prefix for the assembly.
1018 uint32_t value_one = 1;
1019 bool is_little_endian = (reinterpret_cast<char*>(&value_one)[0] == 1);
1020 uint16_t word0x{};
1021 std::memcpy(&word0x, "0x", 2); // we would use bit_cast in C++20 and the
1022 // function could be constexpr.
1023 uint16_t two_first_bytes{};
1024 std::memcpy(&two_first_bytes, input.data(), 2);
1025 if (is_little_endian) {
1026 two_first_bytes |= 0x2000;
1027 } else {
1028 two_first_bytes |= 0x020;
1029 }
1030 return two_first_bytes == word0x;
1031 }
1032
has_hex_prefix(std::string_view input)1033 inline bool has_hex_prefix(std::string_view input) {
1034 return input.size() >= 2 && has_hex_prefix_unsafe(input);
1035 }
1036
is_digit(char x)1037 constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1038
to_lower(char x)1039 constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1040
is_alpha(char x)1041 constexpr bool is_alpha(char x) noexcept {
1042 return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1043 }
1044
is_windows_drive_letter(std::string_view input)1045 inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1046 return input.size() >= 2 &&
1047 (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1048 ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1049 input[2] == '?' || input[2] == '#'));
1050 }
1051
is_normalized_windows_drive_letter(std::string_view input)1052 inline constexpr bool is_normalized_windows_drive_letter(
1053 std::string_view input) noexcept {
1054 return input.size() >= 2 && (is_alpha(input[0]) && (input[1] == ':'));
1055 }
1056
begins_with(std::string_view view,std::string_view prefix)1057 ada_really_inline bool begins_with(std::string_view view,
1058 std::string_view prefix) {
1059 // in C++20, you have view.begins_with(prefix)
1060 // std::equal is constexpr in C++20
1061 return view.size() >= prefix.size() &&
1062 std::equal(prefix.begin(), prefix.end(), view.begin());
1063 }
1064
1065 } // namespace ada::checkers
1066
1067 #endif // ADA_CHECKERS_H
1068 /* end file include/ada/checkers-inl.h */
1069 /* begin file include/ada/log.h */
1070 /**
1071 * @file log.h
1072 * @brief Includes the definitions for logging.
1073 * @private Excluded from docs through the doxygen file.
1074 */
1075 #ifndef ADA_LOG_H
1076 #define ADA_LOG_H
1077
1078 #include <iostream>
1079 // To enable logging, set ADA_LOGGING to 1:
1080 #ifndef ADA_LOGGING
1081 #define ADA_LOGGING 0
1082 #endif
1083
1084 namespace ada {
1085
1086 /**
1087 * Private function used for logging messages.
1088 * @private
1089 */
1090 template <typename T>
inner_log(T t)1091 ada_really_inline void inner_log([[maybe_unused]] T t) {
1092 #if ADA_LOGGING
1093 std::cout << t << std::endl;
1094 #endif
1095 }
1096
1097 /**
1098 * Private function used for logging messages.
1099 * @private
1100 */
1101 template <typename T, typename... Args>
inner_log(T t,Args...args)1102 ada_really_inline void inner_log([[maybe_unused]] T t,
1103 [[maybe_unused]] Args... args) {
1104 #if ADA_LOGGING
1105 std::cout << t;
1106 inner_log(args...);
1107 #endif
1108 }
1109
1110 /**
1111 * Log a message.
1112 * @private
1113 */
1114 template <typename T, typename... Args>
log(T t,Args...args)1115 ada_really_inline void log([[maybe_unused]] T t,
1116 [[maybe_unused]] Args... args) {
1117 #if ADA_LOGGING
1118 std::cout << "ADA_LOG: " << t;
1119 inner_log(args...);
1120 #endif
1121 }
1122
1123 /**
1124 * Log a message.
1125 * @private
1126 */
1127 template <typename T>
log(T t)1128 ada_really_inline void log([[maybe_unused]] T t) {
1129 #if ADA_LOGGING
1130 std::cout << "ADA_LOG: " << t << std::endl;
1131 #endif
1132 }
1133 } // namespace ada
1134
1135 #if ADA_LOGGING
1136
1137 #ifndef ada_log
1138 #define ada_log(...) \
1139 do { \
1140 ada::log(__VA_ARGS__); \
1141 } while (0)
1142 #endif // ada_log
1143 #else
1144 #define ada_log(...)
1145 #endif // ADA_LOGGING
1146
1147 #endif // ADA_LOG_H
1148 /* end file include/ada/log.h */
1149 /* begin file include/ada/encoding_type.h */
1150 /**
1151 * @file encoding_type.h
1152 * @brief Definition for supported encoding types.
1153 */
1154 #ifndef ADA_ENCODING_TYPE_H
1155 #define ADA_ENCODING_TYPE_H
1156
1157 #include <string>
1158
1159 namespace ada {
1160
1161 /**
1162 * This specification defines three encodings with the same names as encoding
1163 * schemes defined in the Unicode standard: UTF-8, UTF-16LE, and UTF-16BE.
1164 *
1165 * @see https://encoding.spec.whatwg.org/#encodings
1166 */
1167 enum class encoding_type {
1168 UTF8,
1169 UTF_16LE,
1170 UTF_16BE,
1171 };
1172
1173 /**
1174 * Convert a encoding_type to string.
1175 */
1176 ada_warn_unused std::string to_string(encoding_type type);
1177
1178 } // namespace ada
1179
1180 #endif // ADA_ENCODING_TYPE_H
1181 /* end file include/ada/encoding_type.h */
1182 /* begin file include/ada/helpers.h */
1183 /**
1184 * @file helpers.h
1185 * @brief Definitions for helper functions used within Ada.
1186 */
1187 #ifndef ADA_HELPERS_H
1188 #define ADA_HELPERS_H
1189
1190 /* begin file include/ada/state.h */
1191 /**
1192 * @file state.h
1193 * @brief Definitions for the states of the URL state machine.
1194 */
1195 #ifndef ADA_STATE_H
1196 #define ADA_STATE_H
1197
1198
1199 #include <string>
1200
1201 namespace ada {
1202
1203 /**
1204 * @see https://url.spec.whatwg.org/#url-parsing
1205 */
1206 enum class state {
1207 AUTHORITY,
1208 SCHEME_START,
1209 SCHEME,
1210 HOST,
1211 NO_SCHEME,
1212 FRAGMENT,
1213 RELATIVE_SCHEME,
1214 RELATIVE_SLASH,
1215 FILE,
1216 FILE_HOST,
1217 FILE_SLASH,
1218 PATH_OR_AUTHORITY,
1219 SPECIAL_AUTHORITY_IGNORE_SLASHES,
1220 SPECIAL_AUTHORITY_SLASHES,
1221 SPECIAL_RELATIVE_OR_AUTHORITY,
1222 QUERY,
1223 PATH,
1224 PATH_START,
1225 OPAQUE_PATH,
1226 PORT,
1227 };
1228
1229 /**
1230 * Stringify a URL state machine state.
1231 */
1232 ada_warn_unused std::string to_string(ada::state s);
1233
1234 } // namespace ada
1235
1236 #endif // ADA_STATE_H
1237 /* end file include/ada/state.h */
1238 /* begin file include/ada/url_base.h */
1239 /**
1240 * @file url_base.h
1241 * @brief Declaration for the basic URL definitions
1242 */
1243 #ifndef ADA_URL_BASE_H
1244 #define ADA_URL_BASE_H
1245
1246 /* begin file include/ada/url_components.h */
1247 /**
1248 * @file url_components.h
1249 * @brief Declaration for the URL Components
1250 */
1251 #ifndef ADA_URL_COMPONENTS_H
1252 #define ADA_URL_COMPONENTS_H
1253
1254
1255 #include <optional>
1256 #include <string_view>
1257
1258 namespace ada {
1259
1260 /**
1261 * @brief URL Component representations using offsets.
1262 *
1263 * @details We design the url_components struct so that it is as small
1264 * and simple as possible. This version uses 32 bytes.
1265 *
1266 * This struct is used to extract components from a single 'href'.
1267 */
1268 struct url_components {
1269 constexpr static uint32_t omitted = uint32_t(-1);
1270
1271 url_components() = default;
1272 url_components(const url_components &u) = default;
1273 url_components(url_components &&u) noexcept = default;
1274 url_components &operator=(url_components &&u) noexcept = default;
1275 url_components &operator=(const url_components &u) = default;
1276 ~url_components() = default;
1277
1278 /*
1279 * By using 32-bit integers, we implicitly assume that the URL string
1280 * cannot exceed 4 GB.
1281 *
1282 * https://user:pass@example.com:1234/foo/bar?baz#quux
1283 * | | | | ^^^^| | |
1284 * | | | | | | | `----- hash_start
1285 * | | | | | | `--------- search_start
1286 * | | | | | `----------------- pathname_start
1287 * | | | | `--------------------- port
1288 * | | | `----------------------- host_end
1289 * | | `---------------------------------- host_start
1290 * | `--------------------------------------- username_end
1291 * `--------------------------------------------- protocol_end
1292 */
1293 uint32_t protocol_end{0};
1294 /**
1295 * Username end is not `omitted` by default to make username and password
1296 * getters less costly to implement.
1297 */
1298 uint32_t username_end{0};
1299 uint32_t host_start{0};
1300 uint32_t host_end{0};
1301 uint32_t port{omitted};
1302 uint32_t pathname_start{0};
1303 uint32_t search_start{omitted};
1304 uint32_t hash_start{omitted};
1305
1306 /**
1307 * Check the following conditions:
1308 * protocol_end < username_end < ... < hash_start,
1309 * expect when a value is omitted. It also computes
1310 * a lower bound on the possible string length that may match these
1311 * offsets.
1312 * @return true if the offset values are
1313 * consistent with a possible URL string
1314 */
1315 [[nodiscard]] bool check_offset_consistency() const noexcept;
1316
1317 /**
1318 * Converts a url_components to JSON stringified version.
1319 */
1320 [[nodiscard]] std::string to_string() const;
1321
1322 }; // struct url_components
1323
1324 } // namespace ada
1325 #endif
1326 /* end file include/ada/url_components.h */
1327 /* begin file include/ada/scheme.h */
1328 /**
1329 * @file scheme.h
1330 * @brief Declarations for the URL scheme.
1331 */
1332 #ifndef ADA_SCHEME_H
1333 #define ADA_SCHEME_H
1334
1335
1336 #include <array>
1337 #include <optional>
1338 #include <string>
1339
1340 /**
1341 * @namespace ada::scheme
1342 * @brief Includes the scheme declarations
1343 */
1344 namespace ada::scheme {
1345
1346 /**
1347 * Type of the scheme as an enum.
1348 * Using strings to represent a scheme type is not ideal because
1349 * checking for types involves string comparisons. It is faster to use
1350 * a simple integer.
1351 * In C++11, we are allowed to specify the underlying type of the enum.
1352 * We pick an 8-bit integer (which allows up to 256 types). Specifying the
1353 * type of the enum may help integration with other systems if the type
1354 * variable is exposed (since its value will not depend on the compiler).
1355 */
1356 enum type : uint8_t {
1357 HTTP = 0,
1358 NOT_SPECIAL = 1,
1359 HTTPS = 2,
1360 WS = 3,
1361 FTP = 4,
1362 WSS = 5,
1363 FILE = 6
1364 };
1365
1366 /**
1367 * A special scheme is an ASCII string that is listed in the first column of the
1368 * following table. The default port for a special scheme is listed in the
1369 * second column on the same row. The default port for any other ASCII string is
1370 * null.
1371 *
1372 * @see https://url.spec.whatwg.org/#url-miscellaneous
1373 * @param scheme
1374 * @return If scheme is a special scheme
1375 */
1376 ada_really_inline constexpr bool is_special(std::string_view scheme);
1377
1378 /**
1379 * A special scheme is an ASCII string that is listed in the first column of the
1380 * following table. The default port for a special scheme is listed in the
1381 * second column on the same row. The default port for any other ASCII string is
1382 * null.
1383 *
1384 * @see https://url.spec.whatwg.org/#url-miscellaneous
1385 * @param scheme
1386 * @return The special port
1387 */
1388 constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1389
1390 /**
1391 * Returns the port number of a special scheme.
1392 * @see https://url.spec.whatwg.org/#special-scheme
1393 */
1394 constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1395 /**
1396 * Returns the scheme of an input, or NOT_SPECIAL if it's not a special scheme
1397 * defined by the spec.
1398 */
1399 constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1400
1401 } // namespace ada::scheme
1402
1403 #endif // ADA_SCHEME_H
1404 /* end file include/ada/scheme.h */
1405
1406 #include <string_view>
1407
1408 namespace ada {
1409
1410 /**
1411 * Type of URL host as an enum.
1412 */
1413 enum url_host_type : uint8_t {
1414 /**
1415 * Represents common URLs such as "https://www.google.com"
1416 */
1417 DEFAULT = 0,
1418 /**
1419 * Represents ipv4 addresses such as "http://127.0.0.1"
1420 */
1421 IPV4 = 1,
1422 /**
1423 * Represents ipv6 addresses such as
1424 * "http://[2001:db8:3333:4444:5555:6666:7777:8888]"
1425 */
1426 IPV6 = 2,
1427 };
1428
1429 /**
1430 * @brief Base class of URL implementations
1431 *
1432 * @details A url_base contains a few attributes: is_valid, has_opaque_path and
1433 * type. All non-trivial implementation details are in derived classes such as
1434 * ada::url and ada::url_aggregator.
1435 *
1436 * It is an abstract class that cannot be instantiated directly.
1437 */
1438 struct url_base {
1439 virtual ~url_base() = default;
1440
1441 /**
1442 * Used for returning the validity from the result of the URL parser.
1443 */
1444 bool is_valid{true};
1445
1446 /**
1447 * A URL has an opaque path if its path is a string.
1448 */
1449 bool has_opaque_path{false};
1450
1451 /**
1452 * URL hosts type
1453 */
1454 url_host_type host_type = url_host_type::DEFAULT;
1455
1456 /**
1457 * @private
1458 */
1459 ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1460
1461 /**
1462 * A URL is special if its scheme is a special scheme. A URL is not special if
1463 * its scheme is not a special scheme.
1464 */
1465 [[nodiscard]] ada_really_inline bool is_special() const noexcept;
1466
1467 /**
1468 * The origin getter steps are to return the serialization of this's URL's
1469 * origin. [HTML]
1470 * @return a newly allocated string.
1471 * @see https://url.spec.whatwg.org/#concept-url-origin
1472 */
1473 [[nodiscard]] virtual std::string get_origin() const noexcept = 0;
1474
1475 /**
1476 * Returns true if this URL has a valid domain as per RFC 1034 and
1477 * corresponding specifications. Among other things, it requires
1478 * that the domain string has fewer than 255 octets.
1479 */
1480 [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1481
1482 /**
1483 * @private
1484 *
1485 * Return the 'special port' if the URL is special and not 'file'.
1486 * Returns 0 otherwise.
1487 */
1488 [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1489
1490 /**
1491 * @private
1492 *
1493 * Get the default port if the url's scheme has one, returns 0 otherwise.
1494 */
1495 [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1496
1497 /**
1498 * @private
1499 *
1500 * Parse a port (16-bit decimal digit) from the provided input.
1501 * We assume that the input does not contain spaces or tabs
1502 * within the ASCII digits.
1503 * It returns how many bytes were consumed when a number is successfully
1504 * parsed.
1505 * @return On failure, it returns zero.
1506 * @see https://url.spec.whatwg.org/#host-parsing
1507 */
1508 virtual size_t parse_port(std::string_view view,
1509 bool check_trailing_content) noexcept = 0;
1510
parse_porturl_base1511 virtual ada_really_inline size_t parse_port(std::string_view view) noexcept {
1512 return this->parse_port(view, false);
1513 }
1514
1515 /**
1516 * Returns a JSON string representation of this URL.
1517 */
1518 [[nodiscard]] virtual std::string to_string() const = 0;
1519
1520 /** @private */
1521 virtual inline void clear_pathname() = 0;
1522
1523 /** @private */
1524 virtual inline void clear_search() = 0;
1525
1526 /** @private */
1527 [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1528
1529 /** @private */
1530 [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1531
1532 }; // url_base
1533
1534 } // namespace ada
1535
1536 #endif
1537 /* end file include/ada/url_base.h */
1538
1539 #include <string_view>
1540 #include <optional>
1541
1542 /**
1543 * @private
1544 * @namespace ada::helpers
1545 * @brief Includes the definitions for helper functions
1546 */
1547 namespace ada::helpers {
1548
1549 /**
1550 * @private
1551 */
1552 template <typename out_iter>
1553 void encode_json(std::string_view view, out_iter out);
1554
1555 /**
1556 * @private
1557 * This function is used to prune a fragment from a url, and returning the
1558 * removed string if input has fragment.
1559 *
1560 * @details prune_hash seeks the first '#' and returns everything after it
1561 * as a string_view, and modifies (in place) the input so that it points at
1562 * everything before the '#'. If no '#' is found, the input is left unchanged
1563 * and std::nullopt is returned.
1564 *
1565 * @attention The function is non-allocating and it does not throw.
1566 * @returns Note that the returned string_view might be empty!
1567 */
1568 ada_really_inline std::optional<std::string_view> prune_hash(
1569 std::string_view& input) noexcept;
1570
1571 /**
1572 * @private
1573 * Defined by the URL specification, shorten a URLs paths.
1574 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1575 * @returns Returns true if path is shortened.
1576 */
1577 ada_really_inline bool shorten_path(std::string& path,
1578 ada::scheme::type type) noexcept;
1579
1580 /**
1581 * @private
1582 * Defined by the URL specification, shorten a URLs paths.
1583 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1584 * @returns Returns true if path is shortened.
1585 */
1586 ada_really_inline bool shorten_path(std::string_view& path,
1587 ada::scheme::type type) noexcept;
1588
1589 /**
1590 * @private
1591 *
1592 * Parse the path from the provided input and append to the existing
1593 * (possibly empty) path. The input cannot contain tabs and spaces: it
1594 * is the user's responsibility to check.
1595 *
1596 * The input is expected to be UTF-8.
1597 *
1598 * @see https://url.spec.whatwg.org/
1599 */
1600 ada_really_inline void parse_prepared_path(std::string_view input,
1601 ada::scheme::type type,
1602 std::string& path);
1603
1604 /**
1605 * @private
1606 * Remove and mutate all ASCII tab or newline characters from an input.
1607 */
1608 ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept;
1609
1610 /**
1611 * @private
1612 * Return the substring from input going from index pos to the end.
1613 * This function cannot throw.
1614 */
1615 ada_really_inline std::string_view substring(std::string_view input,
1616 size_t pos) noexcept;
1617
1618 /**
1619 * @private
1620 * Returns true if the string_view points within the string.
1621 */
1622 bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1623
1624 /**
1625 * @private
1626 * Return the substring from input going from index pos1 to the pos2 (non
1627 * included). The length of the substring is pos2 - pos1.
1628 */
substring(const std::string & input,size_t pos1,size_t pos2)1629 ada_really_inline std::string_view substring(const std::string& input,
1630 size_t pos1,
1631 size_t pos2) noexcept {
1632 #if ADA_DEVELOPMENT_CHECKS
1633 if (pos2 < pos1) {
1634 std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1635 << std::endl;
1636 abort();
1637 }
1638 #endif
1639 return std::string_view(input.data() + pos1, pos2 - pos1);
1640 }
1641
1642 /**
1643 * @private
1644 * Modify the string_view so that it has the new size pos, assuming that pos <=
1645 * input.size(). This function cannot throw.
1646 */
1647 ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1648
1649 /**
1650 * @private
1651 * Returns a host's delimiter location depending on the state of the instance,
1652 * and whether a colon was found outside brackets. Used by the host parser.
1653 */
1654 ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1655 const bool is_special, std::string_view& view) noexcept;
1656
1657 /**
1658 * @private
1659 * Removes leading and trailing C0 control and whitespace characters from
1660 * string.
1661 */
1662 ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept;
1663
1664 /**
1665 * @private
1666 * @see
1667 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1668 */
1669 template <class url_type>
1670 ada_really_inline void strip_trailing_spaces_from_opaque_path(
1671 url_type& url) noexcept;
1672
1673 /**
1674 * @private
1675 * Finds the delimiter of a view in authority state.
1676 */
1677 ada_really_inline size_t
1678 find_authority_delimiter_special(std::string_view view) noexcept;
1679
1680 /**
1681 * @private
1682 * Finds the delimiter of a view in authority state.
1683 */
1684 ada_really_inline size_t
1685 find_authority_delimiter(std::string_view view) noexcept;
1686
1687 /**
1688 * @private
1689 */
1690 template <typename T, typename... Args>
inner_concat(std::string & buffer,T t)1691 inline void inner_concat(std::string& buffer, T t) {
1692 buffer.append(t);
1693 }
1694
1695 /**
1696 * @private
1697 */
1698 template <typename T, typename... Args>
inner_concat(std::string & buffer,T t,Args...args)1699 inline void inner_concat(std::string& buffer, T t, Args... args) {
1700 buffer.append(t);
1701 return inner_concat(buffer, args...);
1702 }
1703
1704 /**
1705 * Concatenate the arguments and return a string.
1706 * @returns a string
1707 */
1708 template <typename... Args>
concat(Args...args)1709 std::string concat(Args... args) {
1710 std::string answer;
1711 inner_concat(answer, args...);
1712 return answer;
1713 }
1714
1715 /**
1716 * @return Number of leading zeroes.
1717 */
leading_zeroes(uint32_t input_num)1718 inline int leading_zeroes(uint32_t input_num) noexcept {
1719 #if ADA_REGULAR_VISUAL_STUDIO
1720 unsigned long leading_zero(0);
1721 unsigned long in(input_num);
1722 return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1723 #else
1724 return __builtin_clz(input_num);
1725 #endif // ADA_REGULAR_VISUAL_STUDIO
1726 }
1727
1728 /**
1729 * Counts the number of decimal digits necessary to represent x.
1730 * faster than std::to_string(x).size().
1731 * @return digit count
1732 */
fast_digit_count(uint32_t x)1733 inline int fast_digit_count(uint32_t x) noexcept {
1734 auto int_log2 = [](uint32_t z) -> int {
1735 return 31 - ada::helpers::leading_zeroes(z | 1);
1736 };
1737 // Compiles to very few instructions. Note that the
1738 // table is static and thus effectively a constant.
1739 // We leave it inside the function because it is meaningless
1740 // outside of it (this comes at no performance cost).
1741 const static uint64_t table[] = {
1742 4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
1743 12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
1744 21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
1745 25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
1746 34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
1747 38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
1748 42949672960, 42949672960};
1749 return int((x + table[int_log2(x)]) >> 32);
1750 }
1751 } // namespace ada::helpers
1752
1753 #endif // ADA_HELPERS_H
1754 /* end file include/ada/helpers.h */
1755 /* begin file include/ada/parser.h */
1756 /**
1757 * @file parser.h
1758 * @brief Definitions for the parser.
1759 */
1760 #ifndef ADA_PARSER_H
1761 #define ADA_PARSER_H
1762
1763 /* begin file include/ada/expected.h */
1764 /**
1765 * @file expected.h
1766 * @brief Definitions for std::expected
1767 * @private Excluded from docs through the doxygen file.
1768 */
1769 ///
1770 // expected - An implementation of std::expected with extensions
1771 // Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
1772 //
1773 // Documentation available at http://tl.tartanllama.xyz/
1774 //
1775 // To the extent possible under law, the author(s) have dedicated all
1776 // copyright and related and neighboring rights to this software to the
1777 // public domain worldwide. This software is distributed without any warranty.
1778 //
1779 // You should have received a copy of the CC0 Public Domain Dedication
1780 // along with this software. If not, see
1781 // <http://creativecommons.org/publicdomain/zero/1.0/>.
1782 ///
1783
1784 #ifndef TL_EXPECTED_HPP
1785 #define TL_EXPECTED_HPP
1786
1787 #define TL_EXPECTED_VERSION_MAJOR 1
1788 #define TL_EXPECTED_VERSION_MINOR 1
1789 #define TL_EXPECTED_VERSION_PATCH 0
1790
1791 #include <exception>
1792 #include <functional>
1793 #include <type_traits>
1794 #include <utility>
1795
1796 #if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
1797 #define TL_EXPECTED_EXCEPTIONS_ENABLED
1798 #endif
1799
1800 #if (defined(_MSC_VER) && _MSC_VER == 1900)
1801 #define TL_EXPECTED_MSVC2015
1802 #define TL_EXPECTED_MSVC2015_CONSTEXPR
1803 #else
1804 #define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
1805 #endif
1806
1807 #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1808 !defined(__clang__))
1809 #define TL_EXPECTED_GCC49
1810 #endif
1811
1812 #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
1813 !defined(__clang__))
1814 #define TL_EXPECTED_GCC54
1815 #endif
1816
1817 #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
1818 !defined(__clang__))
1819 #define TL_EXPECTED_GCC55
1820 #endif
1821
1822 #if !defined(TL_ASSERT)
1823 // can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
1824 #if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
1825 #include <cassert>
1826 #define TL_ASSERT(x) assert(x)
1827 #else
1828 #define TL_ASSERT(x)
1829 #endif
1830 #endif
1831
1832 #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
1833 !defined(__clang__))
1834 // GCC < 5 doesn't support overloading on const&& for member functions
1835
1836 #define TL_EXPECTED_NO_CONSTRR
1837 // GCC < 5 doesn't support some standard C++11 type traits
1838 #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1839 std::has_trivial_copy_constructor<T>
1840 #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1841 std::has_trivial_copy_assign<T>
1842
1843 // This one will be different for GCC 5.7 if it's ever supported
1844 #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1845 std::is_trivially_destructible<T>
1846
1847 // GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
1848 // std::vector for non-copyable types
1849 #elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
1850 #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1851 #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
1852 namespace tl {
1853 namespace detail {
1854 template <class T>
1855 struct is_trivially_copy_constructible
1856 : std::is_trivially_copy_constructible<T> {};
1857 #ifdef _GLIBCXX_VECTOR
1858 template <class T, class A>
1859 struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
1860 #endif
1861 } // namespace detail
1862 } // namespace tl
1863 #endif
1864
1865 #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1866 tl::detail::is_trivially_copy_constructible<T>
1867 #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1868 std::is_trivially_copy_assignable<T>
1869 #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1870 std::is_trivially_destructible<T>
1871 #else
1872 #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
1873 std::is_trivially_copy_constructible<T>
1874 #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
1875 std::is_trivially_copy_assignable<T>
1876 #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
1877 std::is_trivially_destructible<T>
1878 #endif
1879
1880 #if __cplusplus > 201103L
1881 #define TL_EXPECTED_CXX14
1882 #endif
1883
1884 #ifdef TL_EXPECTED_GCC49
1885 #define TL_EXPECTED_GCC49_CONSTEXPR
1886 #else
1887 #define TL_EXPECTED_GCC49_CONSTEXPR constexpr
1888 #endif
1889
1890 #if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
1891 defined(TL_EXPECTED_GCC49))
1892 #define TL_EXPECTED_11_CONSTEXPR
1893 #else
1894 #define TL_EXPECTED_11_CONSTEXPR constexpr
1895 #endif
1896
1897 namespace tl {
1898 template <class T, class E>
1899 class expected;
1900
1901 #ifndef TL_MONOSTATE_INPLACE_MUTEX
1902 #define TL_MONOSTATE_INPLACE_MUTEX
1903 class monostate {};
1904
1905 struct in_place_t {
1906 explicit in_place_t() = default;
1907 };
1908 static constexpr in_place_t in_place{};
1909 #endif
1910
1911 template <class E>
1912 class unexpected {
1913 public:
1914 static_assert(!std::is_same<E, void>::value, "E must not be void");
1915
1916 unexpected() = delete;
1917 constexpr explicit unexpected(const E &e) : m_val(e) {}
1918
1919 constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
1920
1921 template <class... Args, typename std::enable_if<std::is_constructible<
1922 E, Args &&...>::value>::type * = nullptr>
1923 constexpr explicit unexpected(Args &&...args)
1924 : m_val(std::forward<Args>(args)...) {}
1925 template <
1926 class U, class... Args,
1927 typename std::enable_if<std::is_constructible<
1928 E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
1929 constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
1930 : m_val(l, std::forward<Args>(args)...) {}
1931
1932 constexpr const E &value() const & { return m_val; }
1933 TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
1934 TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
1935 constexpr const E &&value() const && { return std::move(m_val); }
1936
1937 private:
1938 E m_val;
1939 };
1940
1941 #ifdef __cpp_deduction_guides
1942 template <class E>
1943 unexpected(E) -> unexpected<E>;
1944 #endif
1945
1946 template <class E>
1947 constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
1948 return lhs.value() == rhs.value();
1949 }
1950 template <class E>
1951 constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
1952 return lhs.value() != rhs.value();
1953 }
1954 template <class E>
1955 constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
1956 return lhs.value() < rhs.value();
1957 }
1958 template <class E>
1959 constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
1960 return lhs.value() <= rhs.value();
1961 }
1962 template <class E>
1963 constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
1964 return lhs.value() > rhs.value();
1965 }
1966 template <class E>
1967 constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
1968 return lhs.value() >= rhs.value();
1969 }
1970
1971 template <class E>
1972 unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
1973 return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
1974 }
1975
1976 struct unexpect_t {
1977 unexpect_t() = default;
1978 };
1979 static constexpr unexpect_t unexpect{};
1980
1981 namespace detail {
1982 template <typename E>
1983 [[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
1984 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
1985 throw std::forward<E>(e);
1986 #else
1987 (void)e;
1988 #ifdef _MSC_VER
1989 __assume(0);
1990 #else
1991 __builtin_unreachable();
1992 #endif
1993 #endif
1994 }
1995
1996 #ifndef TL_TRAITS_MUTEX
1997 #define TL_TRAITS_MUTEX
1998 // C++14-style aliases for brevity
1999 template <class T>
2000 using remove_const_t = typename std::remove_const<T>::type;
2001 template <class T>
2002 using remove_reference_t = typename std::remove_reference<T>::type;
2003 template <class T>
2004 using decay_t = typename std::decay<T>::type;
2005 template <bool E, class T = void>
2006 using enable_if_t = typename std::enable_if<E, T>::type;
2007 template <bool B, class T, class F>
2008 using conditional_t = typename std::conditional<B, T, F>::type;
2009
2010 // std::conjunction from C++17
2011 template <class...>
2012 struct conjunction : std::true_type {};
2013 template <class B>
2014 struct conjunction<B> : B {};
2015 template <class B, class... Bs>
2016 struct conjunction<B, Bs...>
2017 : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2018
2019 #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2020 #define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2021 #endif
2022
2023 // In C++11 mode, there's an issue in libc++'s std::mem_fn
2024 // which results in a hard-error when using it in a noexcept expression
2025 // in some cases. This is a check to workaround the common failing case.
2026 #ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2027 template <class T>
2028 struct is_pointer_to_non_const_member_func : std::false_type {};
2029 template <class T, class Ret, class... Args>
2030 struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2031 : std::true_type {};
2032 template <class T, class Ret, class... Args>
2033 struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2034 : std::true_type {};
2035 template <class T, class Ret, class... Args>
2036 struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2037 : std::true_type {};
2038 template <class T, class Ret, class... Args>
2039 struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2040 : std::true_type {};
2041 template <class T, class Ret, class... Args>
2042 struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &>
2043 : std::true_type {};
2044 template <class T, class Ret, class... Args>
2045 struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
2046 : std::true_type {};
2047
2048 template <class T>
2049 struct is_const_or_const_ref : std::false_type {};
2050 template <class T>
2051 struct is_const_or_const_ref<T const &> : std::true_type {};
2052 template <class T>
2053 struct is_const_or_const_ref<T const> : std::true_type {};
2054 #endif
2055
2056 // std::invoke from C++17
2057 // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2058 template <
2059 typename Fn, typename... Args,
2060 #ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2061 typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2062 is_const_or_const_ref<Args...>::value)>,
2063 #endif
2064 typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2065 constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
2066 noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2067 -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2068 return std::mem_fn(f)(std::forward<Args>(args)...);
2069 }
2070
2071 template <typename Fn, typename... Args,
2072 typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2073 constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
2074 noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2075 -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2076 return std::forward<Fn>(f)(std::forward<Args>(args)...);
2077 }
2078
2079 // std::invoke_result from C++17
2080 template <class F, class, class... Us>
2081 struct invoke_result_impl;
2082
2083 template <class F, class... Us>
2084 struct invoke_result_impl<
2085 F,
2086 decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2087 Us...> {
2088 using type =
2089 decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2090 };
2091
2092 template <class F, class... Us>
2093 using invoke_result = invoke_result_impl<F, void, Us...>;
2094
2095 template <class F, class... Us>
2096 using invoke_result_t = typename invoke_result<F, Us...>::type;
2097
2098 #if defined(_MSC_VER) && _MSC_VER <= 1900
2099 // TODO make a version which works with MSVC 2015
2100 template <class T, class U = T>
2101 struct is_swappable : std::true_type {};
2102
2103 template <class T, class U = T>
2104 struct is_nothrow_swappable : std::true_type {};
2105 #else
2106 // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2107 namespace swap_adl_tests {
2108 // if swap ADL finds this then it would call std::swap otherwise (same
2109 // signature)
2110 struct tag {};
2111
2112 template <class T>
2113 tag swap(T &, T &);
2114 template <class T, std::size_t N>
2115 tag swap(T (&a)[N], T (&b)[N]);
2116
2117 // helper functions to test if an unqualified swap is possible, and if it
2118 // becomes std::swap
2119 template <class, class>
2120 std::false_type can_swap(...) noexcept(false);
2121 template <class T, class U,
2122 class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
2123 std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
2124 std::declval<U &>())));
2125
2126 template <class, class>
2127 std::false_type uses_std(...);
2128 template <class T, class U>
2129 std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
2130 uses_std(int);
2131
2132 template <class T>
2133 struct is_std_swap_noexcept
2134 : std::integral_constant<bool,
2135 std::is_nothrow_move_constructible<T>::value &&
2136 std::is_nothrow_move_assignable<T>::value> {};
2137
2138 template <class T, std::size_t N>
2139 struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2140
2141 template <class T, class U>
2142 struct is_adl_swap_noexcept
2143 : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2144 } // namespace swap_adl_tests
2145
2146 template <class T, class U = T>
2147 struct is_swappable
2148 : std::integral_constant<
2149 bool,
2150 decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2151 (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2152 (std::is_move_assignable<T>::value &&
2153 std::is_move_constructible<T>::value))> {};
2154
2155 template <class T, std::size_t N>
2156 struct is_swappable<T[N], T[N]>
2157 : std::integral_constant<
2158 bool,
2159 decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2160 (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2161 0))::value ||
2162 is_swappable<T, T>::value)> {};
2163
2164 template <class T, class U = T>
2165 struct is_nothrow_swappable
2166 : std::integral_constant<
2167 bool,
2168 is_swappable<T, U>::value &&
2169 ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2170 detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2171 (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2172 detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2173 #endif
2174 #endif
2175
2176 // Trait for checking if a type is a tl::expected
2177 template <class T>
2178 struct is_expected_impl : std::false_type {};
2179 template <class T, class E>
2180 struct is_expected_impl<expected<T, E>> : std::true_type {};
2181 template <class T>
2182 using is_expected = is_expected_impl<decay_t<T>>;
2183
2184 template <class T, class E, class U>
2185 using expected_enable_forward_value = detail::enable_if_t<
2186 std::is_constructible<T, U &&>::value &&
2187 !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2188 !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2189 !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2190
2191 template <class T, class E, class U, class G, class UR, class GR>
2192 using expected_enable_from_other = detail::enable_if_t<
2193 std::is_constructible<T, UR>::value &&
2194 std::is_constructible<E, GR>::value &&
2195 !std::is_constructible<T, expected<U, G> &>::value &&
2196 !std::is_constructible<T, expected<U, G> &&>::value &&
2197 !std::is_constructible<T, const expected<U, G> &>::value &&
2198 !std::is_constructible<T, const expected<U, G> &&>::value &&
2199 !std::is_convertible<expected<U, G> &, T>::value &&
2200 !std::is_convertible<expected<U, G> &&, T>::value &&
2201 !std::is_convertible<const expected<U, G> &, T>::value &&
2202 !std::is_convertible<const expected<U, G> &&, T>::value>;
2203
2204 template <class T, class U>
2205 using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2206
2207 template <class T>
2208 using is_copy_constructible_or_void =
2209 is_void_or<T, std::is_copy_constructible<T>>;
2210
2211 template <class T>
2212 using is_move_constructible_or_void =
2213 is_void_or<T, std::is_move_constructible<T>>;
2214
2215 template <class T>
2216 using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2217
2218 template <class T>
2219 using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2220
2221 } // namespace detail
2222
2223 namespace detail {
2224 struct no_init_t {};
2225 static constexpr no_init_t no_init{};
2226
2227 // Implements the storage of the values, and ensures that the destructor is
2228 // trivial if it can be.
2229 //
2230 // This specialization is for where neither `T` or `E` is trivially
2231 // destructible, so the destructors must be called on destruction of the
2232 // `expected`
2233 template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2234 bool = std::is_trivially_destructible<E>::value>
2235 struct expected_storage_base {
2236 constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2237 constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2238
2239 template <class... Args,
2240 detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2241 nullptr>
2242 constexpr expected_storage_base(in_place_t, Args &&...args)
2243 : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2244
2245 template <class U, class... Args,
2246 detail::enable_if_t<std::is_constructible<
2247 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2248 constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2249 Args &&...args)
2250 : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2251 template <class... Args,
2252 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2253 nullptr>
2254 constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2255 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2256
2257 template <class U, class... Args,
2258 detail::enable_if_t<std::is_constructible<
2259 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2260 constexpr explicit expected_storage_base(unexpect_t,
2261 std::initializer_list<U> il,
2262 Args &&...args)
2263 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2264
2265 ~expected_storage_base() {
2266 if (m_has_val) {
2267 m_val.~T();
2268 } else {
2269 m_unexpect.~unexpected<E>();
2270 }
2271 }
2272 union {
2273 T m_val;
2274 unexpected<E> m_unexpect;
2275 char m_no_init;
2276 };
2277 bool m_has_val;
2278 };
2279
2280 // This specialization is for when both `T` and `E` are trivially-destructible,
2281 // so the destructor of the `expected` can be trivial.
2282 template <class T, class E>
2283 struct expected_storage_base<T, E, true, true> {
2284 constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2285 constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2286
2287 template <class... Args,
2288 detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2289 nullptr>
2290 constexpr expected_storage_base(in_place_t, Args &&...args)
2291 : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2292
2293 template <class U, class... Args,
2294 detail::enable_if_t<std::is_constructible<
2295 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2296 constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2297 Args &&...args)
2298 : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2299 template <class... Args,
2300 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2301 nullptr>
2302 constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2303 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2304
2305 template <class U, class... Args,
2306 detail::enable_if_t<std::is_constructible<
2307 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2308 constexpr explicit expected_storage_base(unexpect_t,
2309 std::initializer_list<U> il,
2310 Args &&...args)
2311 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2312
2313 ~expected_storage_base() = default;
2314 union {
2315 T m_val;
2316 unexpected<E> m_unexpect;
2317 char m_no_init;
2318 };
2319 bool m_has_val;
2320 };
2321
2322 // T is trivial, E is not.
2323 template <class T, class E>
2324 struct expected_storage_base<T, E, true, false> {
2325 constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2326 TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2327 : m_no_init(), m_has_val(false) {}
2328
2329 template <class... Args,
2330 detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2331 nullptr>
2332 constexpr expected_storage_base(in_place_t, Args &&...args)
2333 : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2334
2335 template <class U, class... Args,
2336 detail::enable_if_t<std::is_constructible<
2337 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2338 constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2339 Args &&...args)
2340 : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2341 template <class... Args,
2342 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2343 nullptr>
2344 constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2345 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2346
2347 template <class U, class... Args,
2348 detail::enable_if_t<std::is_constructible<
2349 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2350 constexpr explicit expected_storage_base(unexpect_t,
2351 std::initializer_list<U> il,
2352 Args &&...args)
2353 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2354
2355 ~expected_storage_base() {
2356 if (!m_has_val) {
2357 m_unexpect.~unexpected<E>();
2358 }
2359 }
2360
2361 union {
2362 T m_val;
2363 unexpected<E> m_unexpect;
2364 char m_no_init;
2365 };
2366 bool m_has_val;
2367 };
2368
2369 // E is trivial, T is not.
2370 template <class T, class E>
2371 struct expected_storage_base<T, E, false, true> {
2372 constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2373 constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2374
2375 template <class... Args,
2376 detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
2377 nullptr>
2378 constexpr expected_storage_base(in_place_t, Args &&...args)
2379 : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2380
2381 template <class U, class... Args,
2382 detail::enable_if_t<std::is_constructible<
2383 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2384 constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2385 Args &&...args)
2386 : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2387 template <class... Args,
2388 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2389 nullptr>
2390 constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2391 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2392
2393 template <class U, class... Args,
2394 detail::enable_if_t<std::is_constructible<
2395 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2396 constexpr explicit expected_storage_base(unexpect_t,
2397 std::initializer_list<U> il,
2398 Args &&...args)
2399 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2400
2401 ~expected_storage_base() {
2402 if (m_has_val) {
2403 m_val.~T();
2404 }
2405 }
2406 union {
2407 T m_val;
2408 unexpected<E> m_unexpect;
2409 char m_no_init;
2410 };
2411 bool m_has_val;
2412 };
2413
2414 // `T` is `void`, `E` is trivially-destructible
2415 template <class E>
2416 struct expected_storage_base<void, E, false, true> {
2417 #if __GNUC__ <= 5
2418 // no constexpr for GCC 4/5 bug
2419 #else
2420 TL_EXPECTED_MSVC2015_CONSTEXPR
2421 #endif
2422 expected_storage_base() : m_has_val(true) {}
2423
2424 constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2425
2426 constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2427
2428 template <class... Args,
2429 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2430 nullptr>
2431 constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2432 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2433
2434 template <class U, class... Args,
2435 detail::enable_if_t<std::is_constructible<
2436 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2437 constexpr explicit expected_storage_base(unexpect_t,
2438 std::initializer_list<U> il,
2439 Args &&...args)
2440 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2441
2442 ~expected_storage_base() = default;
2443 struct dummy {};
2444 union {
2445 unexpected<E> m_unexpect;
2446 dummy m_val;
2447 };
2448 bool m_has_val;
2449 };
2450
2451 // `T` is `void`, `E` is not trivially-destructible
2452 template <class E>
2453 struct expected_storage_base<void, E, false, false> {
2454 constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2455 constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2456
2457 constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2458
2459 template <class... Args,
2460 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
2461 nullptr>
2462 constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
2463 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2464
2465 template <class U, class... Args,
2466 detail::enable_if_t<std::is_constructible<
2467 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
2468 constexpr explicit expected_storage_base(unexpect_t,
2469 std::initializer_list<U> il,
2470 Args &&...args)
2471 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2472
2473 ~expected_storage_base() {
2474 if (!m_has_val) {
2475 m_unexpect.~unexpected<E>();
2476 }
2477 }
2478
2479 union {
2480 unexpected<E> m_unexpect;
2481 char m_dummy;
2482 };
2483 bool m_has_val;
2484 };
2485
2486 // This base class provides some handy member functions which can be used in
2487 // further derived classes
2488 template <class T, class E>
2489 struct expected_operations_base : expected_storage_base<T, E> {
2490 using expected_storage_base<T, E>::expected_storage_base;
2491
2492 template <class... Args>
2493 void construct(Args &&...args) noexcept {
2494 new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2495 this->m_has_val = true;
2496 }
2497
2498 template <class Rhs>
2499 void construct_with(Rhs &&rhs) noexcept {
2500 new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2501 this->m_has_val = true;
2502 }
2503
2504 template <class... Args>
2505 void construct_error(Args &&...args) noexcept {
2506 new (std::addressof(this->m_unexpect))
2507 unexpected<E>(std::forward<Args>(args)...);
2508 this->m_has_val = false;
2509 }
2510
2511 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2512
2513 // These assign overloads ensure that the most efficient assignment
2514 // implementation is used while maintaining the strong exception guarantee.
2515 // The problematic case is where rhs has a value, but *this does not.
2516 //
2517 // This overload handles the case where we can just copy-construct `T`
2518 // directly into place without throwing.
2519 template <class U = T,
2520 detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
2521 * = nullptr>
2522 void assign(const expected_operations_base &rhs) noexcept {
2523 if (!this->m_has_val && rhs.m_has_val) {
2524 geterr().~unexpected<E>();
2525 construct(rhs.get());
2526 } else {
2527 assign_common(rhs);
2528 }
2529 }
2530
2531 // This overload handles the case where we can attempt to create a copy of
2532 // `T`, then no-throw move it into place if the copy was successful.
2533 template <class U = T,
2534 detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2535 std::is_nothrow_move_constructible<U>::value>
2536 * = nullptr>
2537 void assign(const expected_operations_base &rhs) noexcept {
2538 if (!this->m_has_val && rhs.m_has_val) {
2539 T tmp = rhs.get();
2540 geterr().~unexpected<E>();
2541 construct(std::move(tmp));
2542 } else {
2543 assign_common(rhs);
2544 }
2545 }
2546
2547 // This overload is the worst-case, where we have to move-construct the
2548 // unexpected value into temporary storage, then try to copy the T into place.
2549 // If the construction succeeds, then everything is fine, but if it throws,
2550 // then we move the old unexpected value back into place before rethrowing the
2551 // exception.
2552 template <class U = T,
2553 detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2554 !std::is_nothrow_move_constructible<U>::value>
2555 * = nullptr>
2556 void assign(const expected_operations_base &rhs) {
2557 if (!this->m_has_val && rhs.m_has_val) {
2558 auto tmp = std::move(geterr());
2559 geterr().~unexpected<E>();
2560
2561 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2562 try {
2563 construct(rhs.get());
2564 } catch (...) {
2565 geterr() = std::move(tmp);
2566 throw;
2567 }
2568 #else
2569 construct(rhs.get());
2570 #endif
2571 } else {
2572 assign_common(rhs);
2573 }
2574 }
2575
2576 // These overloads do the same as above, but for rvalues
2577 template <class U = T,
2578 detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
2579 * = nullptr>
2580 void assign(expected_operations_base &&rhs) noexcept {
2581 if (!this->m_has_val && rhs.m_has_val) {
2582 geterr().~unexpected<E>();
2583 construct(std::move(rhs).get());
2584 } else {
2585 assign_common(std::move(rhs));
2586 }
2587 }
2588
2589 template <class U = T,
2590 detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
2591 * = nullptr>
2592 void assign(expected_operations_base &&rhs) {
2593 if (!this->m_has_val && rhs.m_has_val) {
2594 auto tmp = std::move(geterr());
2595 geterr().~unexpected<E>();
2596 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2597 try {
2598 construct(std::move(rhs).get());
2599 } catch (...) {
2600 geterr() = std::move(tmp);
2601 throw;
2602 }
2603 #else
2604 construct(std::move(rhs).get());
2605 #endif
2606 } else {
2607 assign_common(std::move(rhs));
2608 }
2609 }
2610
2611 #else
2612
2613 // If exceptions are disabled then we can just copy-construct
2614 void assign(const expected_operations_base &rhs) noexcept {
2615 if (!this->m_has_val && rhs.m_has_val) {
2616 geterr().~unexpected<E>();
2617 construct(rhs.get());
2618 } else {
2619 assign_common(rhs);
2620 }
2621 }
2622
2623 void assign(expected_operations_base &&rhs) noexcept {
2624 if (!this->m_has_val && rhs.m_has_val) {
2625 geterr().~unexpected<E>();
2626 construct(std::move(rhs).get());
2627 } else {
2628 assign_common(std::move(rhs));
2629 }
2630 }
2631
2632 #endif
2633
2634 // The common part of move/copy assigning
2635 template <class Rhs>
2636 void assign_common(Rhs &&rhs) {
2637 if (this->m_has_val) {
2638 if (rhs.m_has_val) {
2639 get() = std::forward<Rhs>(rhs).get();
2640 } else {
2641 destroy_val();
2642 construct_error(std::forward<Rhs>(rhs).geterr());
2643 }
2644 } else {
2645 if (!rhs.m_has_val) {
2646 geterr() = std::forward<Rhs>(rhs).geterr();
2647 }
2648 }
2649 }
2650
2651 bool has_value() const { return this->m_has_val; }
2652
2653 TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
2654 constexpr const T &get() const & { return this->m_val; }
2655 TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
2656 #ifndef TL_EXPECTED_NO_CONSTRR
2657 constexpr const T &&get() const && { return std::move(this->m_val); }
2658 #endif
2659
2660 TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
2661 return this->m_unexpect;
2662 }
2663 constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
2664 TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
2665 return std::move(this->m_unexpect);
2666 }
2667 #ifndef TL_EXPECTED_NO_CONSTRR
2668 constexpr const unexpected<E> &&geterr() const && {
2669 return std::move(this->m_unexpect);
2670 }
2671 #endif
2672
2673 TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2674 };
2675
2676 // This base class provides some handy member functions which can be used in
2677 // further derived classes
2678 template <class E>
2679 struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2680 using expected_storage_base<void, E>::expected_storage_base;
2681
2682 template <class... Args>
2683 void construct() noexcept {
2684 this->m_has_val = true;
2685 }
2686
2687 // This function doesn't use its argument, but needs it so that code in
2688 // levels above this can work independently of whether T is void
2689 template <class Rhs>
2690 void construct_with(Rhs &&) noexcept {
2691 this->m_has_val = true;
2692 }
2693
2694 template <class... Args>
2695 void construct_error(Args &&...args) noexcept {
2696 new (std::addressof(this->m_unexpect))
2697 unexpected<E>(std::forward<Args>(args)...);
2698 this->m_has_val = false;
2699 }
2700
2701 template <class Rhs>
2702 void assign(Rhs &&rhs) noexcept {
2703 if (!this->m_has_val) {
2704 if (rhs.m_has_val) {
2705 geterr().~unexpected<E>();
2706 construct();
2707 } else {
2708 geterr() = std::forward<Rhs>(rhs).geterr();
2709 }
2710 } else {
2711 if (!rhs.m_has_val) {
2712 construct_error(std::forward<Rhs>(rhs).geterr());
2713 }
2714 }
2715 }
2716
2717 bool has_value() const { return this->m_has_val; }
2718
2719 TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
2720 return this->m_unexpect;
2721 }
2722 constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
2723 TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
2724 return std::move(this->m_unexpect);
2725 }
2726 #ifndef TL_EXPECTED_NO_CONSTRR
2727 constexpr const unexpected<E> &&geterr() const && {
2728 return std::move(this->m_unexpect);
2729 }
2730 #endif
2731
2732 TL_EXPECTED_11_CONSTEXPR void destroy_val() {
2733 // no-op
2734 }
2735 };
2736
2737 // This class manages conditionally having a trivial copy constructor
2738 // This specialization is for when T and E are trivially copy constructible
2739 template <class T, class E,
2740 bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
2741 value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
2742 struct expected_copy_base : expected_operations_base<T, E> {
2743 using expected_operations_base<T, E>::expected_operations_base;
2744 };
2745
2746 // This specialization is for when T or E are not trivially copy constructible
2747 template <class T, class E>
2748 struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
2749 using expected_operations_base<T, E>::expected_operations_base;
2750
2751 expected_copy_base() = default;
2752 expected_copy_base(const expected_copy_base &rhs)
2753 : expected_operations_base<T, E>(no_init) {
2754 if (rhs.has_value()) {
2755 this->construct_with(rhs);
2756 } else {
2757 this->construct_error(rhs.geterr());
2758 }
2759 }
2760
2761 expected_copy_base(expected_copy_base &&rhs) = default;
2762 expected_copy_base &operator=(const expected_copy_base &rhs) = default;
2763 expected_copy_base &operator=(expected_copy_base &&rhs) = default;
2764 };
2765
2766 // This class manages conditionally having a trivial move constructor
2767 // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2768 // doesn't implement an analogue to std::is_trivially_move_constructible. We
2769 // have to make do with a non-trivial move constructor even if T is trivially
2770 // move constructible
2771 #ifndef TL_EXPECTED_GCC49
2772 template <class T, class E,
2773 bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
2774 &&std::is_trivially_move_constructible<E>::value>
2775 struct expected_move_base : expected_copy_base<T, E> {
2776 using expected_copy_base<T, E>::expected_copy_base;
2777 };
2778 #else
2779 template <class T, class E, bool = false>
2780 struct expected_move_base;
2781 #endif
2782 template <class T, class E>
2783 struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
2784 using expected_copy_base<T, E>::expected_copy_base;
2785
2786 expected_move_base() = default;
2787 expected_move_base(const expected_move_base &rhs) = default;
2788
2789 expected_move_base(expected_move_base &&rhs) noexcept(
2790 std::is_nothrow_move_constructible<T>::value)
2791 : expected_copy_base<T, E>(no_init) {
2792 if (rhs.has_value()) {
2793 this->construct_with(std::move(rhs));
2794 } else {
2795 this->construct_error(std::move(rhs.geterr()));
2796 }
2797 }
2798 expected_move_base &operator=(const expected_move_base &rhs) = default;
2799 expected_move_base &operator=(expected_move_base &&rhs) = default;
2800 };
2801
2802 // This class manages conditionally having a trivial copy assignment operator
2803 template <class T, class E,
2804 bool = is_void_or<
2805 T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
2806 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
2807 TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
2808 &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value
2809 &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value
2810 &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
2811 struct expected_copy_assign_base : expected_move_base<T, E> {
2812 using expected_move_base<T, E>::expected_move_base;
2813 };
2814
2815 template <class T, class E>
2816 struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
2817 using expected_move_base<T, E>::expected_move_base;
2818
2819 expected_copy_assign_base() = default;
2820 expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
2821
2822 expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
2823 expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
2824 this->assign(rhs);
2825 return *this;
2826 }
2827 expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) =
2828 default;
2829 };
2830
2831 // This class manages conditionally having a trivial move assignment operator
2832 // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2833 // doesn't implement an analogue to std::is_trivially_move_assignable. We have
2834 // to make do with a non-trivial move assignment operator even if T is trivially
2835 // move assignable
2836 #ifndef TL_EXPECTED_GCC49
2837 template <class T, class E,
2838 bool =
2839 is_void_or<T, conjunction<std::is_trivially_destructible<T>,
2840 std::is_trivially_move_constructible<T>,
2841 std::is_trivially_move_assignable<T>>>::
2842 value &&std::is_trivially_destructible<E>::value
2843 &&std::is_trivially_move_constructible<E>::value
2844 &&std::is_trivially_move_assignable<E>::value>
2845 struct expected_move_assign_base : expected_copy_assign_base<T, E> {
2846 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2847 };
2848 #else
2849 template <class T, class E, bool = false>
2850 struct expected_move_assign_base;
2851 #endif
2852
2853 template <class T, class E>
2854 struct expected_move_assign_base<T, E, false>
2855 : expected_copy_assign_base<T, E> {
2856 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
2857
2858 expected_move_assign_base() = default;
2859 expected_move_assign_base(const expected_move_assign_base &rhs) = default;
2860
2861 expected_move_assign_base(expected_move_assign_base &&rhs) = default;
2862
2863 expected_move_assign_base &operator=(const expected_move_assign_base &rhs) =
2864 default;
2865
2866 expected_move_assign_base &
2867 operator=(expected_move_assign_base &&rhs) noexcept(
2868 std::is_nothrow_move_constructible<T>::value
2869 &&std::is_nothrow_move_assignable<T>::value) {
2870 this->assign(std::move(rhs));
2871 return *this;
2872 }
2873 };
2874
2875 // expected_delete_ctor_base will conditionally delete copy and move
2876 // constructors depending on whether T is copy/move constructible
2877 template <class T, class E,
2878 bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
2879 std::is_copy_constructible<E>::value),
2880 bool EnableMove = (is_move_constructible_or_void<T>::value &&
2881 std::is_move_constructible<E>::value)>
2882 struct expected_delete_ctor_base {
2883 expected_delete_ctor_base() = default;
2884 expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
2885 expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
2886 expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2887 default;
2888 expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2889 default;
2890 };
2891
2892 template <class T, class E>
2893 struct expected_delete_ctor_base<T, E, true, false> {
2894 expected_delete_ctor_base() = default;
2895 expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
2896 expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
2897 expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2898 default;
2899 expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2900 default;
2901 };
2902
2903 template <class T, class E>
2904 struct expected_delete_ctor_base<T, E, false, true> {
2905 expected_delete_ctor_base() = default;
2906 expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
2907 expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
2908 expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2909 default;
2910 expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2911 default;
2912 };
2913
2914 template <class T, class E>
2915 struct expected_delete_ctor_base<T, E, false, false> {
2916 expected_delete_ctor_base() = default;
2917 expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
2918 expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
2919 expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) =
2920 default;
2921 expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept =
2922 default;
2923 };
2924
2925 // expected_delete_assign_base will conditionally delete copy and move
2926 // constructors depending on whether T and E are copy/move constructible +
2927 // assignable
2928 template <class T, class E,
2929 bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
2930 std::is_copy_constructible<E>::value &&
2931 is_copy_assignable_or_void<T>::value &&
2932 std::is_copy_assignable<E>::value),
2933 bool EnableMove = (is_move_constructible_or_void<T>::value &&
2934 std::is_move_constructible<E>::value &&
2935 is_move_assignable_or_void<T>::value &&
2936 std::is_move_assignable<E>::value)>
2937 struct expected_delete_assign_base {
2938 expected_delete_assign_base() = default;
2939 expected_delete_assign_base(const expected_delete_assign_base &) = default;
2940 expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
2941 default;
2942 expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
2943 default;
2944 expected_delete_assign_base &operator=(
2945 expected_delete_assign_base &&) noexcept = default;
2946 };
2947
2948 template <class T, class E>
2949 struct expected_delete_assign_base<T, E, true, false> {
2950 expected_delete_assign_base() = default;
2951 expected_delete_assign_base(const expected_delete_assign_base &) = default;
2952 expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
2953 default;
2954 expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
2955 default;
2956 expected_delete_assign_base &operator=(
2957 expected_delete_assign_base &&) noexcept = delete;
2958 };
2959
2960 template <class T, class E>
2961 struct expected_delete_assign_base<T, E, false, true> {
2962 expected_delete_assign_base() = default;
2963 expected_delete_assign_base(const expected_delete_assign_base &) = default;
2964 expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
2965 default;
2966 expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
2967 delete;
2968 expected_delete_assign_base &operator=(
2969 expected_delete_assign_base &&) noexcept = default;
2970 };
2971
2972 template <class T, class E>
2973 struct expected_delete_assign_base<T, E, false, false> {
2974 expected_delete_assign_base() = default;
2975 expected_delete_assign_base(const expected_delete_assign_base &) = default;
2976 expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
2977 default;
2978 expected_delete_assign_base &operator=(const expected_delete_assign_base &) =
2979 delete;
2980 expected_delete_assign_base &operator=(
2981 expected_delete_assign_base &&) noexcept = delete;
2982 };
2983
2984 // This is needed to be able to construct the expected_default_ctor_base which
2985 // follows, while still conditionally deleting the default constructor.
2986 struct default_constructor_tag {
2987 explicit constexpr default_constructor_tag() = default;
2988 };
2989
2990 // expected_default_ctor_base will ensure that expected has a deleted default
2991 // consturctor if T is not default constructible.
2992 // This specialization is for when T is default constructible
2993 template <class T, class E,
2994 bool Enable =
2995 std::is_default_constructible<T>::value || std::is_void<T>::value>
2996 struct expected_default_ctor_base {
2997 constexpr expected_default_ctor_base() noexcept = default;
2998 constexpr expected_default_ctor_base(
2999 expected_default_ctor_base const &) noexcept = default;
3000 constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
3001 default;
3002 expected_default_ctor_base &operator=(
3003 expected_default_ctor_base const &) noexcept = default;
3004 expected_default_ctor_base &operator=(
3005 expected_default_ctor_base &&) noexcept = default;
3006
3007 constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3008 };
3009
3010 // This specialization is for when T is not default constructible
3011 template <class T, class E>
3012 struct expected_default_ctor_base<T, E, false> {
3013 constexpr expected_default_ctor_base() noexcept = delete;
3014 constexpr expected_default_ctor_base(
3015 expected_default_ctor_base const &) noexcept = default;
3016 constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
3017 default;
3018 expected_default_ctor_base &operator=(
3019 expected_default_ctor_base const &) noexcept = default;
3020 expected_default_ctor_base &operator=(
3021 expected_default_ctor_base &&) noexcept = default;
3022
3023 constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3024 };
3025 } // namespace detail
3026
3027 template <class E>
3028 class bad_expected_access : public std::exception {
3029 public:
3030 explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3031
3032 virtual const char *what() const noexcept override {
3033 return "Bad expected access";
3034 }
3035
3036 const E &error() const & { return m_val; }
3037 E &error() & { return m_val; }
3038 const E &&error() const && { return std::move(m_val); }
3039 E &&error() && { return std::move(m_val); }
3040
3041 private:
3042 E m_val;
3043 };
3044
3045 /// An `expected<T, E>` object is an object that contains the storage for
3046 /// another object and manages the lifetime of this contained object `T`.
3047 /// Alternatively it could contain the storage for another unexpected object
3048 /// `E`. The contained object may not be initialized after the expected object
3049 /// has been initialized, and may not be destroyed before the expected object
3050 /// has been destroyed. The initialization state of the contained object is
3051 /// tracked by the expected object.
3052 template <class T, class E>
3053 class expected : private detail::expected_move_assign_base<T, E>,
3054 private detail::expected_delete_ctor_base<T, E>,
3055 private detail::expected_delete_assign_base<T, E>,
3056 private detail::expected_default_ctor_base<T, E> {
3057 static_assert(!std::is_reference<T>::value, "T must not be a reference");
3058 static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3059 "T must not be in_place_t");
3060 static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3061 "T must not be unexpect_t");
3062 static_assert(
3063 !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3064 "T must not be unexpected<E>");
3065 static_assert(!std::is_reference<E>::value, "E must not be a reference");
3066
3067 T *valptr() { return std::addressof(this->m_val); }
3068 const T *valptr() const { return std::addressof(this->m_val); }
3069 unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
3070 const unexpected<E> *errptr() const {
3071 return std::addressof(this->m_unexpect);
3072 }
3073
3074 template <class U = T,
3075 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3076 TL_EXPECTED_11_CONSTEXPR U &val() {
3077 return this->m_val;
3078 }
3079 TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
3080
3081 template <class U = T,
3082 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3083 constexpr const U &val() const {
3084 return this->m_val;
3085 }
3086 constexpr const unexpected<E> &err() const { return this->m_unexpect; }
3087
3088 using impl_base = detail::expected_move_assign_base<T, E>;
3089 using ctor_base = detail::expected_default_ctor_base<T, E>;
3090
3091 public:
3092 typedef T value_type;
3093 typedef E error_type;
3094 typedef unexpected<E> unexpected_type;
3095
3096 #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3097 !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3098 template <class F>
3099 TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
3100 return and_then_impl(*this, std::forward<F>(f));
3101 }
3102 template <class F>
3103 TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
3104 return and_then_impl(std::move(*this), std::forward<F>(f));
3105 }
3106 template <class F>
3107 constexpr auto and_then(F &&f) const & {
3108 return and_then_impl(*this, std::forward<F>(f));
3109 }
3110
3111 #ifndef TL_EXPECTED_NO_CONSTRR
3112 template <class F>
3113 constexpr auto and_then(F &&f) const && {
3114 return and_then_impl(std::move(*this), std::forward<F>(f));
3115 }
3116 #endif
3117
3118 #else
3119 template <class F>
3120 TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl(
3121 std::declval<expected &>(), std::forward<F>(f))) {
3122 return and_then_impl(*this, std::forward<F>(f));
3123 }
3124 template <class F>
3125 TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl(
3126 std::declval<expected &&>(), std::forward<F>(f))) {
3127 return and_then_impl(std::move(*this), std::forward<F>(f));
3128 }
3129 template <class F>
3130 constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
3131 std::declval<expected const &>(), std::forward<F>(f))) {
3132 return and_then_impl(*this, std::forward<F>(f));
3133 }
3134
3135 #ifndef TL_EXPECTED_NO_CONSTRR
3136 template <class F>
3137 constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
3138 std::declval<expected const &&>(), std::forward<F>(f))) {
3139 return and_then_impl(std::move(*this), std::forward<F>(f));
3140 }
3141 #endif
3142 #endif
3143
3144 #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3145 !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3146 template <class F>
3147 TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
3148 return expected_map_impl(*this, std::forward<F>(f));
3149 }
3150 template <class F>
3151 TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
3152 return expected_map_impl(std::move(*this), std::forward<F>(f));
3153 }
3154 template <class F>
3155 constexpr auto map(F &&f) const & {
3156 return expected_map_impl(*this, std::forward<F>(f));
3157 }
3158 template <class F>
3159 constexpr auto map(F &&f) const && {
3160 return expected_map_impl(std::move(*this), std::forward<F>(f));
3161 }
3162 #else
3163 template <class F>
3164 TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3165 std::declval<expected &>(), std::declval<F &&>()))
3166 map(F &&f) & {
3167 return expected_map_impl(*this, std::forward<F>(f));
3168 }
3169 template <class F>
3170 TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3171 std::declval<F &&>()))
3172 map(F &&f) && {
3173 return expected_map_impl(std::move(*this), std::forward<F>(f));
3174 }
3175 template <class F>
3176 constexpr decltype(expected_map_impl(std::declval<const expected &>(),
3177 std::declval<F &&>()))
3178 map(F &&f) const & {
3179 return expected_map_impl(*this, std::forward<F>(f));
3180 }
3181
3182 #ifndef TL_EXPECTED_NO_CONSTRR
3183 template <class F>
3184 constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
3185 std::declval<F &&>()))
3186 map(F &&f) const && {
3187 return expected_map_impl(std::move(*this), std::forward<F>(f));
3188 }
3189 #endif
3190 #endif
3191
3192 #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3193 !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3194 template <class F>
3195 TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
3196 return expected_map_impl(*this, std::forward<F>(f));
3197 }
3198 template <class F>
3199 TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
3200 return expected_map_impl(std::move(*this), std::forward<F>(f));
3201 }
3202 template <class F>
3203 constexpr auto transform(F &&f) const & {
3204 return expected_map_impl(*this, std::forward<F>(f));
3205 }
3206 template <class F>
3207 constexpr auto transform(F &&f) const && {
3208 return expected_map_impl(std::move(*this), std::forward<F>(f));
3209 }
3210 #else
3211 template <class F>
3212 TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3213 std::declval<expected &>(), std::declval<F &&>()))
3214 transform(F &&f) & {
3215 return expected_map_impl(*this, std::forward<F>(f));
3216 }
3217 template <class F>
3218 TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3219 std::declval<F &&>()))
3220 transform(F &&f) && {
3221 return expected_map_impl(std::move(*this), std::forward<F>(f));
3222 }
3223 template <class F>
3224 constexpr decltype(expected_map_impl(std::declval<const expected &>(),
3225 std::declval<F &&>()))
3226 transform(F &&f) const & {
3227 return expected_map_impl(*this, std::forward<F>(f));
3228 }
3229
3230 #ifndef TL_EXPECTED_NO_CONSTRR
3231 template <class F>
3232 constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
3233 std::declval<F &&>()))
3234 transform(F &&f) const && {
3235 return expected_map_impl(std::move(*this), std::forward<F>(f));
3236 }
3237 #endif
3238 #endif
3239
3240 #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3241 !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3242 template <class F>
3243 TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
3244 return map_error_impl(*this, std::forward<F>(f));
3245 }
3246 template <class F>
3247 TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
3248 return map_error_impl(std::move(*this), std::forward<F>(f));
3249 }
3250 template <class F>
3251 constexpr auto map_error(F &&f) const & {
3252 return map_error_impl(*this, std::forward<F>(f));
3253 }
3254 template <class F>
3255 constexpr auto map_error(F &&f) const && {
3256 return map_error_impl(std::move(*this), std::forward<F>(f));
3257 }
3258 #else
3259 template <class F>
3260 TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
3261 std::declval<F &&>()))
3262 map_error(F &&f) & {
3263 return map_error_impl(*this, std::forward<F>(f));
3264 }
3265 template <class F>
3266 TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
3267 std::declval<F &&>()))
3268 map_error(F &&f) && {
3269 return map_error_impl(std::move(*this), std::forward<F>(f));
3270 }
3271 template <class F>
3272 constexpr decltype(map_error_impl(std::declval<const expected &>(),
3273 std::declval<F &&>()))
3274 map_error(F &&f) const & {
3275 return map_error_impl(*this, std::forward<F>(f));
3276 }
3277
3278 #ifndef TL_EXPECTED_NO_CONSTRR
3279 template <class F>
3280 constexpr decltype(map_error_impl(std::declval<const expected &&>(),
3281 std::declval<F &&>()))
3282 map_error(F &&f) const && {
3283 return map_error_impl(std::move(*this), std::forward<F>(f));
3284 }
3285 #endif
3286 #endif
3287 #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3288 !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3289 template <class F>
3290 TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
3291 return map_error_impl(*this, std::forward<F>(f));
3292 }
3293 template <class F>
3294 TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
3295 return map_error_impl(std::move(*this), std::forward<F>(f));
3296 }
3297 template <class F>
3298 constexpr auto transform_error(F &&f) const & {
3299 return map_error_impl(*this, std::forward<F>(f));
3300 }
3301 template <class F>
3302 constexpr auto transform_error(F &&f) const && {
3303 return map_error_impl(std::move(*this), std::forward<F>(f));
3304 }
3305 #else
3306 template <class F>
3307 TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
3308 std::declval<F &&>()))
3309 transform_error(F &&f) & {
3310 return map_error_impl(*this, std::forward<F>(f));
3311 }
3312 template <class F>
3313 TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
3314 std::declval<F &&>()))
3315 transform_error(F &&f) && {
3316 return map_error_impl(std::move(*this), std::forward<F>(f));
3317 }
3318 template <class F>
3319 constexpr decltype(map_error_impl(std::declval<const expected &>(),
3320 std::declval<F &&>()))
3321 transform_error(F &&f) const & {
3322 return map_error_impl(*this, std::forward<F>(f));
3323 }
3324
3325 #ifndef TL_EXPECTED_NO_CONSTRR
3326 template <class F>
3327 constexpr decltype(map_error_impl(std::declval<const expected &&>(),
3328 std::declval<F &&>()))
3329 transform_error(F &&f) const && {
3330 return map_error_impl(std::move(*this), std::forward<F>(f));
3331 }
3332 #endif
3333 #endif
3334 template <class F>
3335 expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
3336 return or_else_impl(*this, std::forward<F>(f));
3337 }
3338
3339 template <class F>
3340 expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
3341 return or_else_impl(std::move(*this), std::forward<F>(f));
3342 }
3343
3344 template <class F>
3345 expected constexpr or_else(F &&f) const & {
3346 return or_else_impl(*this, std::forward<F>(f));
3347 }
3348
3349 #ifndef TL_EXPECTED_NO_CONSTRR
3350 template <class F>
3351 expected constexpr or_else(F &&f) const && {
3352 return or_else_impl(std::move(*this), std::forward<F>(f));
3353 }
3354 #endif
3355 constexpr expected() = default;
3356 constexpr expected(const expected &rhs) = default;
3357 constexpr expected(expected &&rhs) = default;
3358 expected &operator=(const expected &rhs) = default;
3359 expected &operator=(expected &&rhs) = default;
3360
3361 template <class... Args,
3362 detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
3363 nullptr>
3364 constexpr expected(in_place_t, Args &&...args)
3365 : impl_base(in_place, std::forward<Args>(args)...),
3366 ctor_base(detail::default_constructor_tag{}) {}
3367
3368 template <class U, class... Args,
3369 detail::enable_if_t<std::is_constructible<
3370 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3371 constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
3372 : impl_base(in_place, il, std::forward<Args>(args)...),
3373 ctor_base(detail::default_constructor_tag{}) {}
3374
3375 template <class G = E,
3376 detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
3377 nullptr,
3378 detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
3379 nullptr>
3380 explicit constexpr expected(const unexpected<G> &e)
3381 : impl_base(unexpect, e.value()),
3382 ctor_base(detail::default_constructor_tag{}) {}
3383
3384 template <
3385 class G = E,
3386 detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
3387 nullptr,
3388 detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
3389 constexpr expected(unexpected<G> const &e)
3390 : impl_base(unexpect, e.value()),
3391 ctor_base(detail::default_constructor_tag{}) {}
3392
3393 template <
3394 class G = E,
3395 detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
3396 detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
3397 explicit constexpr expected(unexpected<G> &&e) noexcept(
3398 std::is_nothrow_constructible<E, G &&>::value)
3399 : impl_base(unexpect, std::move(e.value())),
3400 ctor_base(detail::default_constructor_tag{}) {}
3401
3402 template <
3403 class G = E,
3404 detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
3405 detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
3406 constexpr expected(unexpected<G> &&e) noexcept(
3407 std::is_nothrow_constructible<E, G &&>::value)
3408 : impl_base(unexpect, std::move(e.value())),
3409 ctor_base(detail::default_constructor_tag{}) {}
3410
3411 template <class... Args,
3412 detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
3413 nullptr>
3414 constexpr explicit expected(unexpect_t, Args &&...args)
3415 : impl_base(unexpect, std::forward<Args>(args)...),
3416 ctor_base(detail::default_constructor_tag{}) {}
3417
3418 template <class U, class... Args,
3419 detail::enable_if_t<std::is_constructible<
3420 E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3421 constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3422 Args &&...args)
3423 : impl_base(unexpect, il, std::forward<Args>(args)...),
3424 ctor_base(detail::default_constructor_tag{}) {}
3425
3426 template <class U, class G,
3427 detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
3428 std::is_convertible<G const &, E>::value)> * =
3429 nullptr,
3430 detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
3431 * = nullptr>
3432 explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
3433 : ctor_base(detail::default_constructor_tag{}) {
3434 if (rhs.has_value()) {
3435 this->construct(*rhs);
3436 } else {
3437 this->construct_error(rhs.error());
3438 }
3439 }
3440
3441 template <class U, class G,
3442 detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
3443 std::is_convertible<G const &, E>::value)> * =
3444 nullptr,
3445 detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
3446 * = nullptr>
3447 TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
3448 : ctor_base(detail::default_constructor_tag{}) {
3449 if (rhs.has_value()) {
3450 this->construct(*rhs);
3451 } else {
3452 this->construct_error(rhs.error());
3453 }
3454 }
3455
3456 template <
3457 class U, class G,
3458 detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
3459 std::is_convertible<G &&, E>::value)> * = nullptr,
3460 detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
3461 explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
3462 : ctor_base(detail::default_constructor_tag{}) {
3463 if (rhs.has_value()) {
3464 this->construct(std::move(*rhs));
3465 } else {
3466 this->construct_error(std::move(rhs.error()));
3467 }
3468 }
3469
3470 template <
3471 class U, class G,
3472 detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
3473 std::is_convertible<G &&, E>::value)> * = nullptr,
3474 detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
3475 TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
3476 : ctor_base(detail::default_constructor_tag{}) {
3477 if (rhs.has_value()) {
3478 this->construct(std::move(*rhs));
3479 } else {
3480 this->construct_error(std::move(rhs.error()));
3481 }
3482 }
3483
3484 template <
3485 class U = T,
3486 detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
3487 detail::expected_enable_forward_value<T, E, U> * = nullptr>
3488 explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
3489 : expected(in_place, std::forward<U>(v)) {}
3490
3491 template <
3492 class U = T,
3493 detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
3494 detail::expected_enable_forward_value<T, E, U> * = nullptr>
3495 TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
3496 : expected(in_place, std::forward<U>(v)) {}
3497
3498 template <
3499 class U = T, class G = T,
3500 detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
3501 nullptr,
3502 detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
3503 detail::enable_if_t<
3504 (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3505 !detail::conjunction<std::is_scalar<T>,
3506 std::is_same<T, detail::decay_t<U>>>::value &&
3507 std::is_constructible<T, U>::value &&
3508 std::is_assignable<G &, U>::value &&
3509 std::is_nothrow_move_constructible<E>::value)> * = nullptr>
3510 expected &operator=(U &&v) {
3511 if (has_value()) {
3512 val() = std::forward<U>(v);
3513 } else {
3514 err().~unexpected<E>();
3515 ::new (valptr()) T(std::forward<U>(v));
3516 this->m_has_val = true;
3517 }
3518
3519 return *this;
3520 }
3521
3522 template <
3523 class U = T, class G = T,
3524 detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
3525 nullptr,
3526 detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
3527 detail::enable_if_t<
3528 (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3529 !detail::conjunction<std::is_scalar<T>,
3530 std::is_same<T, detail::decay_t<U>>>::value &&
3531 std::is_constructible<T, U>::value &&
3532 std::is_assignable<G &, U>::value &&
3533 std::is_nothrow_move_constructible<E>::value)> * = nullptr>
3534 expected &operator=(U &&v) {
3535 if (has_value()) {
3536 val() = std::forward<U>(v);
3537 } else {
3538 auto tmp = std::move(err());
3539 err().~unexpected<E>();
3540
3541 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3542 try {
3543 ::new (valptr()) T(std::forward<U>(v));
3544 this->m_has_val = true;
3545 } catch (...) {
3546 err() = std::move(tmp);
3547 throw;
3548 }
3549 #else
3550 ::new (valptr()) T(std::forward<U>(v));
3551 this->m_has_val = true;
3552 #endif
3553 }
3554
3555 return *this;
3556 }
3557
3558 template <class G = E,
3559 detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3560 std::is_assignable<G &, G>::value> * = nullptr>
3561 expected &operator=(const unexpected<G> &rhs) {
3562 if (!has_value()) {
3563 err() = rhs;
3564 } else {
3565 this->destroy_val();
3566 ::new (errptr()) unexpected<E>(rhs);
3567 this->m_has_val = false;
3568 }
3569
3570 return *this;
3571 }
3572
3573 template <class G = E,
3574 detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3575 std::is_move_assignable<G>::value> * = nullptr>
3576 expected &operator=(unexpected<G> &&rhs) noexcept {
3577 if (!has_value()) {
3578 err() = std::move(rhs);
3579 } else {
3580 this->destroy_val();
3581 ::new (errptr()) unexpected<E>(std::move(rhs));
3582 this->m_has_val = false;
3583 }
3584
3585 return *this;
3586 }
3587
3588 template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3589 T, Args &&...>::value> * = nullptr>
3590 void emplace(Args &&...args) {
3591 if (has_value()) {
3592 val().~T();
3593 } else {
3594 err().~unexpected<E>();
3595 this->m_has_val = true;
3596 }
3597 ::new (valptr()) T(std::forward<Args>(args)...);
3598 }
3599
3600 template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3601 T, Args &&...>::value> * = nullptr>
3602 void emplace(Args &&...args) {
3603 if (has_value()) {
3604 val().~T();
3605 ::new (valptr()) T(std::forward<Args>(args)...);
3606 } else {
3607 auto tmp = std::move(err());
3608 err().~unexpected<E>();
3609
3610 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3611 try {
3612 ::new (valptr()) T(std::forward<Args>(args)...);
3613 this->m_has_val = true;
3614 } catch (...) {
3615 err() = std::move(tmp);
3616 throw;
3617 }
3618 #else
3619 ::new (valptr()) T(std::forward<Args>(args)...);
3620 this->m_has_val = true;
3621 #endif
3622 }
3623 }
3624
3625 template <class U, class... Args,
3626 detail::enable_if_t<std::is_nothrow_constructible<
3627 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3628 void emplace(std::initializer_list<U> il, Args &&...args) {
3629 if (has_value()) {
3630 T t(il, std::forward<Args>(args)...);
3631 val() = std::move(t);
3632 } else {
3633 err().~unexpected<E>();
3634 ::new (valptr()) T(il, std::forward<Args>(args)...);
3635 this->m_has_val = true;
3636 }
3637 }
3638
3639 template <class U, class... Args,
3640 detail::enable_if_t<!std::is_nothrow_constructible<
3641 T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
3642 void emplace(std::initializer_list<U> il, Args &&...args) {
3643 if (has_value()) {
3644 T t(il, std::forward<Args>(args)...);
3645 val() = std::move(t);
3646 } else {
3647 auto tmp = std::move(err());
3648 err().~unexpected<E>();
3649
3650 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3651 try {
3652 ::new (valptr()) T(il, std::forward<Args>(args)...);
3653 this->m_has_val = true;
3654 } catch (...) {
3655 err() = std::move(tmp);
3656 throw;
3657 }
3658 #else
3659 ::new (valptr()) T(il, std::forward<Args>(args)...);
3660 this->m_has_val = true;
3661 #endif
3662 }
3663 }
3664
3665 private:
3666 using t_is_void = std::true_type;
3667 using t_is_not_void = std::false_type;
3668 using t_is_nothrow_move_constructible = std::true_type;
3669 using move_constructing_t_can_throw = std::false_type;
3670 using e_is_nothrow_move_constructible = std::true_type;
3671 using move_constructing_e_can_throw = std::false_type;
3672
3673 void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
3674 // swapping void is a no-op
3675 }
3676
3677 void swap_where_both_have_value(expected &rhs, t_is_not_void) {
3678 using std::swap;
3679 swap(val(), rhs.val());
3680 }
3681
3682 void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
3683 std::is_nothrow_move_constructible<E>::value) {
3684 ::new (errptr()) unexpected_type(std::move(rhs.err()));
3685 rhs.err().~unexpected_type();
3686 std::swap(this->m_has_val, rhs.m_has_val);
3687 }
3688
3689 void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
3690 swap_where_only_one_has_value_and_t_is_not_void(
3691 rhs, typename std::is_nothrow_move_constructible<T>::type{},
3692 typename std::is_nothrow_move_constructible<E>::type{});
3693 }
3694
3695 void swap_where_only_one_has_value_and_t_is_not_void(
3696 expected &rhs, t_is_nothrow_move_constructible,
3697 e_is_nothrow_move_constructible) noexcept {
3698 auto temp = std::move(val());
3699 val().~T();
3700 ::new (errptr()) unexpected_type(std::move(rhs.err()));
3701 rhs.err().~unexpected_type();
3702 ::new (rhs.valptr()) T(std::move(temp));
3703 std::swap(this->m_has_val, rhs.m_has_val);
3704 }
3705
3706 void swap_where_only_one_has_value_and_t_is_not_void(
3707 expected &rhs, t_is_nothrow_move_constructible,
3708 move_constructing_e_can_throw) {
3709 auto temp = std::move(val());
3710 val().~T();
3711 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3712 try {
3713 ::new (errptr()) unexpected_type(std::move(rhs.err()));
3714 rhs.err().~unexpected_type();
3715 ::new (rhs.valptr()) T(std::move(temp));
3716 std::swap(this->m_has_val, rhs.m_has_val);
3717 } catch (...) {
3718 val() = std::move(temp);
3719 throw;
3720 }
3721 #else
3722 ::new (errptr()) unexpected_type(std::move(rhs.err()));
3723 rhs.err().~unexpected_type();
3724 ::new (rhs.valptr()) T(std::move(temp));
3725 std::swap(this->m_has_val, rhs.m_has_val);
3726 #endif
3727 }
3728
3729 void swap_where_only_one_has_value_and_t_is_not_void(
3730 expected &rhs, move_constructing_t_can_throw,
3731 e_is_nothrow_move_constructible) {
3732 auto temp = std::move(rhs.err());
3733 rhs.err().~unexpected_type();
3734 #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3735 try {
3736 ::new (rhs.valptr()) T(std::move(val()));
3737 val().~T();
3738 ::new (errptr()) unexpected_type(std::move(temp));
3739 std::swap(this->m_has_val, rhs.m_has_val);
3740 } catch (...) {
3741 rhs.err() = std::move(temp);
3742 throw;
3743 }
3744 #else
3745 ::new (rhs.valptr()) T(std::move(val()));
3746 val().~T();
3747 ::new (errptr()) unexpected_type(std::move(temp));
3748 std::swap(this->m_has_val, rhs.m_has_val);
3749 #endif
3750 }
3751
3752 public:
3753 template <class OT = T, class OE = E>
3754 detail::enable_if_t<detail::is_swappable<OT>::value &&
3755 detail::is_swappable<OE>::value &&
3756 (std::is_nothrow_move_constructible<OT>::value ||
3757 std::is_nothrow_move_constructible<OE>::value)>
3758 swap(expected &rhs) noexcept(
3759 std::is_nothrow_move_constructible<T>::value
3760 &&detail::is_nothrow_swappable<T>::value
3761 &&std::is_nothrow_move_constructible<E>::value
3762 &&detail::is_nothrow_swappable<E>::value) {
3763 if (has_value() && rhs.has_value()) {
3764 swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
3765 } else if (!has_value() && rhs.has_value()) {
3766 rhs.swap(*this);
3767 } else if (has_value()) {
3768 swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
3769 } else {
3770 using std::swap;
3771 swap(err(), rhs.err());
3772 }
3773 }
3774
3775 constexpr const T *operator->() const {
3776 TL_ASSERT(has_value());
3777 return valptr();
3778 }
3779 TL_EXPECTED_11_CONSTEXPR T *operator->() {
3780 TL_ASSERT(has_value());
3781 return valptr();
3782 }
3783
3784 template <class U = T,
3785 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3786 constexpr const U &operator*() const & {
3787 TL_ASSERT(has_value());
3788 return val();
3789 }
3790 template <class U = T,
3791 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3792 TL_EXPECTED_11_CONSTEXPR U &operator*() & {
3793 TL_ASSERT(has_value());
3794 return val();
3795 }
3796 template <class U = T,
3797 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3798 constexpr const U &&operator*() const && {
3799 TL_ASSERT(has_value());
3800 return std::move(val());
3801 }
3802 template <class U = T,
3803 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3804 TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
3805 TL_ASSERT(has_value());
3806 return std::move(val());
3807 }
3808
3809 constexpr bool has_value() const noexcept { return this->m_has_val; }
3810 constexpr explicit operator bool() const noexcept { return this->m_has_val; }
3811
3812 template <class U = T,
3813 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3814 TL_EXPECTED_11_CONSTEXPR const U &value() const & {
3815 if (!has_value())
3816 detail::throw_exception(bad_expected_access<E>(err().value()));
3817 return val();
3818 }
3819 template <class U = T,
3820 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3821 TL_EXPECTED_11_CONSTEXPR U &value() & {
3822 if (!has_value())
3823 detail::throw_exception(bad_expected_access<E>(err().value()));
3824 return val();
3825 }
3826 template <class U = T,
3827 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3828 TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
3829 if (!has_value())
3830 detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3831 return std::move(val());
3832 }
3833 template <class U = T,
3834 detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
3835 TL_EXPECTED_11_CONSTEXPR U &&value() && {
3836 if (!has_value())
3837 detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
3838 return std::move(val());
3839 }
3840
3841 constexpr const E &error() const & {
3842 TL_ASSERT(!has_value());
3843 return err().value();
3844 }
3845 TL_EXPECTED_11_CONSTEXPR E &error() & {
3846 TL_ASSERT(!has_value());
3847 return err().value();
3848 }
3849 constexpr const E &&error() const && {
3850 TL_ASSERT(!has_value());
3851 return std::move(err().value());
3852 }
3853 TL_EXPECTED_11_CONSTEXPR E &&error() && {
3854 TL_ASSERT(!has_value());
3855 return std::move(err().value());
3856 }
3857
3858 template <class U>
3859 constexpr T value_or(U &&v) const & {
3860 static_assert(std::is_copy_constructible<T>::value &&
3861 std::is_convertible<U &&, T>::value,
3862 "T must be copy-constructible and convertible to from U&&");
3863 return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
3864 }
3865 template <class U>
3866 TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
3867 static_assert(std::is_move_constructible<T>::value &&
3868 std::is_convertible<U &&, T>::value,
3869 "T must be move-constructible and convertible to from U&&");
3870 return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
3871 }
3872 };
3873
3874 namespace detail {
3875 template <class Exp>
3876 using exp_t = typename detail::decay_t<Exp>::value_type;
3877 template <class Exp>
3878 using err_t = typename detail::decay_t<Exp>::error_type;
3879 template <class Exp, class Ret>
3880 using ret_t = expected<Ret, err_t<Exp>>;
3881
3882 #ifdef TL_EXPECTED_CXX14
3883 template <class Exp, class F,
3884 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3885 class Ret = decltype(detail::invoke(std::declval<F>(),
3886 *std::declval<Exp>()))>
3887 constexpr auto and_then_impl(Exp &&exp, F &&f) {
3888 static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3889
3890 return exp.has_value()
3891 ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
3892 : Ret(unexpect, std::forward<Exp>(exp).error());
3893 }
3894
3895 template <class Exp, class F,
3896 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
3897 class Ret = decltype(detail::invoke(std::declval<F>()))>
3898 constexpr auto and_then_impl(Exp &&exp, F &&f) {
3899 static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3900
3901 return exp.has_value() ? detail::invoke(std::forward<F>(f))
3902 : Ret(unexpect, std::forward<Exp>(exp).error());
3903 }
3904 #else
3905 template <class>
3906 struct TC;
3907 template <class Exp, class F,
3908 class Ret = decltype(detail::invoke(std::declval<F>(),
3909 *std::declval<Exp>())),
3910 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
3911 auto and_then_impl(Exp &&exp, F &&f) -> Ret {
3912 static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3913
3914 return exp.has_value()
3915 ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
3916 : Ret(unexpect, std::forward<Exp>(exp).error());
3917 }
3918
3919 template <class Exp, class F,
3920 class Ret = decltype(detail::invoke(std::declval<F>())),
3921 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
3922 constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
3923 static_assert(detail::is_expected<Ret>::value, "F must return an expected");
3924
3925 return exp.has_value() ? detail::invoke(std::forward<F>(f))
3926 : Ret(unexpect, std::forward<Exp>(exp).error());
3927 }
3928 #endif
3929
3930 #ifdef TL_EXPECTED_CXX14
3931 template <class Exp, class F,
3932 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3933 class Ret = decltype(detail::invoke(std::declval<F>(),
3934 *std::declval<Exp>())),
3935 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
3936 constexpr auto expected_map_impl(Exp &&exp, F &&f) {
3937 using result = ret_t<Exp, detail::decay_t<Ret>>;
3938 return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
3939 *std::forward<Exp>(exp)))
3940 : result(unexpect, std::forward<Exp>(exp).error());
3941 }
3942
3943 template <class Exp, class F,
3944 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3945 class Ret = decltype(detail::invoke(std::declval<F>(),
3946 *std::declval<Exp>())),
3947 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
3948 auto expected_map_impl(Exp &&exp, F &&f) {
3949 using result = expected<void, err_t<Exp>>;
3950 if (exp.has_value()) {
3951 detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
3952 return result();
3953 }
3954
3955 return result(unexpect, std::forward<Exp>(exp).error());
3956 }
3957
3958 template <class Exp, class F,
3959 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
3960 class Ret = decltype(detail::invoke(std::declval<F>())),
3961 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
3962 constexpr auto expected_map_impl(Exp &&exp, F &&f) {
3963 using result = ret_t<Exp, detail::decay_t<Ret>>;
3964 return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
3965 : result(unexpect, std::forward<Exp>(exp).error());
3966 }
3967
3968 template <class Exp, class F,
3969 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
3970 class Ret = decltype(detail::invoke(std::declval<F>())),
3971 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
3972 auto expected_map_impl(Exp &&exp, F &&f) {
3973 using result = expected<void, err_t<Exp>>;
3974 if (exp.has_value()) {
3975 detail::invoke(std::forward<F>(f));
3976 return result();
3977 }
3978
3979 return result(unexpect, std::forward<Exp>(exp).error());
3980 }
3981 #else
3982 template <class Exp, class F,
3983 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3984 class Ret = decltype(detail::invoke(std::declval<F>(),
3985 *std::declval<Exp>())),
3986 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
3987
3988 constexpr auto expected_map_impl(Exp &&exp, F &&f)
3989 -> ret_t<Exp, detail::decay_t<Ret>> {
3990 using result = ret_t<Exp, detail::decay_t<Ret>>;
3991
3992 return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
3993 *std::forward<Exp>(exp)))
3994 : result(unexpect, std::forward<Exp>(exp).error());
3995 }
3996
3997 template <class Exp, class F,
3998 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
3999 class Ret = decltype(detail::invoke(std::declval<F>(),
4000 *std::declval<Exp>())),
4001 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4002
4003 auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
4004 if (exp.has_value()) {
4005 detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4006 return {};
4007 }
4008
4009 return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4010 }
4011
4012 template <class Exp, class F,
4013 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4014 class Ret = decltype(detail::invoke(std::declval<F>())),
4015 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4016
4017 constexpr auto expected_map_impl(Exp &&exp, F &&f)
4018 -> ret_t<Exp, detail::decay_t<Ret>> {
4019 using result = ret_t<Exp, detail::decay_t<Ret>>;
4020
4021 return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4022 : result(unexpect, std::forward<Exp>(exp).error());
4023 }
4024
4025 template <class Exp, class F,
4026 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4027 class Ret = decltype(detail::invoke(std::declval<F>())),
4028 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4029
4030 auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
4031 if (exp.has_value()) {
4032 detail::invoke(std::forward<F>(f));
4033 return {};
4034 }
4035
4036 return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4037 }
4038 #endif
4039
4040 #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4041 !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4042 template <class Exp, class F,
4043 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4044 class Ret = decltype(detail::invoke(std::declval<F>(),
4045 std::declval<Exp>().error())),
4046 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4047 constexpr auto map_error_impl(Exp &&exp, F &&f) {
4048 using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4049 return exp.has_value()
4050 ? result(*std::forward<Exp>(exp))
4051 : result(unexpect, detail::invoke(std::forward<F>(f),
4052 std::forward<Exp>(exp).error()));
4053 }
4054 template <class Exp, class F,
4055 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4056 class Ret = decltype(detail::invoke(std::declval<F>(),
4057 std::declval<Exp>().error())),
4058 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4059 auto map_error_impl(Exp &&exp, F &&f) {
4060 using result = expected<exp_t<Exp>, monostate>;
4061 if (exp.has_value()) {
4062 return result(*std::forward<Exp>(exp));
4063 }
4064
4065 detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4066 return result(unexpect, monostate{});
4067 }
4068 template <class Exp, class F,
4069 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4070 class Ret = decltype(detail::invoke(std::declval<F>(),
4071 std::declval<Exp>().error())),
4072 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4073 constexpr auto map_error_impl(Exp &&exp, F &&f) {
4074 using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4075 return exp.has_value()
4076 ? result()
4077 : result(unexpect, detail::invoke(std::forward<F>(f),
4078 std::forward<Exp>(exp).error()));
4079 }
4080 template <class Exp, class F,
4081 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4082 class Ret = decltype(detail::invoke(std::declval<F>(),
4083 std::declval<Exp>().error())),
4084 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4085 auto map_error_impl(Exp &&exp, F &&f) {
4086 using result = expected<exp_t<Exp>, monostate>;
4087 if (exp.has_value()) {
4088 return result();
4089 }
4090
4091 detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4092 return result(unexpect, monostate{});
4093 }
4094 #else
4095 template <class Exp, class F,
4096 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4097 class Ret = decltype(detail::invoke(std::declval<F>(),
4098 std::declval<Exp>().error())),
4099 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4100 constexpr auto map_error_impl(Exp &&exp, F &&f)
4101 -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4102 using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4103
4104 return exp.has_value()
4105 ? result(*std::forward<Exp>(exp))
4106 : result(unexpect, detail::invoke(std::forward<F>(f),
4107 std::forward<Exp>(exp).error()));
4108 }
4109
4110 template <class Exp, class F,
4111 detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
4112 class Ret = decltype(detail::invoke(std::declval<F>(),
4113 std::declval<Exp>().error())),
4114 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4115 auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
4116 using result = expected<exp_t<Exp>, monostate>;
4117 if (exp.has_value()) {
4118 return result(*std::forward<Exp>(exp));
4119 }
4120
4121 detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4122 return result(unexpect, monostate{});
4123 }
4124
4125 template <class Exp, class F,
4126 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4127 class Ret = decltype(detail::invoke(std::declval<F>(),
4128 std::declval<Exp>().error())),
4129 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4130 constexpr auto map_error_impl(Exp &&exp, F &&f)
4131 -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4132 using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4133
4134 return exp.has_value()
4135 ? result()
4136 : result(unexpect, detail::invoke(std::forward<F>(f),
4137 std::forward<Exp>(exp).error()));
4138 }
4139
4140 template <class Exp, class F,
4141 detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
4142 class Ret = decltype(detail::invoke(std::declval<F>(),
4143 std::declval<Exp>().error())),
4144 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4145 auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
4146 using result = expected<exp_t<Exp>, monostate>;
4147 if (exp.has_value()) {
4148 return result();
4149 }
4150
4151 detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4152 return result(unexpect, monostate{});
4153 }
4154 #endif
4155
4156 #ifdef TL_EXPECTED_CXX14
4157 template <class Exp, class F,
4158 class Ret = decltype(detail::invoke(std::declval<F>(),
4159 std::declval<Exp>().error())),
4160 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4161 constexpr auto or_else_impl(Exp &&exp, F &&f) {
4162 static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4163 return exp.has_value() ? std::forward<Exp>(exp)
4164 : detail::invoke(std::forward<F>(f),
4165 std::forward<Exp>(exp).error());
4166 }
4167
4168 template <class Exp, class F,
4169 class Ret = decltype(detail::invoke(std::declval<F>(),
4170 std::declval<Exp>().error())),
4171 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4172 detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
4173 return exp.has_value() ? std::forward<Exp>(exp)
4174 : (detail::invoke(std::forward<F>(f),
4175 std::forward<Exp>(exp).error()),
4176 std::forward<Exp>(exp));
4177 }
4178 #else
4179 template <class Exp, class F,
4180 class Ret = decltype(detail::invoke(std::declval<F>(),
4181 std::declval<Exp>().error())),
4182 detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
4183 auto or_else_impl(Exp &&exp, F &&f) -> Ret {
4184 static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4185 return exp.has_value() ? std::forward<Exp>(exp)
4186 : detail::invoke(std::forward<F>(f),
4187 std::forward<Exp>(exp).error());
4188 }
4189
4190 template <class Exp, class F,
4191 class Ret = decltype(detail::invoke(std::declval<F>(),
4192 std::declval<Exp>().error())),
4193 detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
4194 detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
4195 return exp.has_value() ? std::forward<Exp>(exp)
4196 : (detail::invoke(std::forward<F>(f),
4197 std::forward<Exp>(exp).error()),
4198 std::forward<Exp>(exp));
4199 }
4200 #endif
4201 } // namespace detail
4202
4203 template <class T, class E, class U, class F>
4204 constexpr bool operator==(const expected<T, E> &lhs,
4205 const expected<U, F> &rhs) {
4206 return (lhs.has_value() != rhs.has_value())
4207 ? false
4208 : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4209 }
4210 template <class T, class E, class U, class F>
4211 constexpr bool operator!=(const expected<T, E> &lhs,
4212 const expected<U, F> &rhs) {
4213 return (lhs.has_value() != rhs.has_value())
4214 ? true
4215 : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4216 }
4217 template <class E, class F>
4218 constexpr bool operator==(const expected<void, E> &lhs,
4219 const expected<void, F> &rhs) {
4220 return (lhs.has_value() != rhs.has_value())
4221 ? false
4222 : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4223 }
4224 template <class E, class F>
4225 constexpr bool operator!=(const expected<void, E> &lhs,
4226 const expected<void, F> &rhs) {
4227 return (lhs.has_value() != rhs.has_value())
4228 ? true
4229 : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4230 }
4231
4232 template <class T, class E, class U>
4233 constexpr bool operator==(const expected<T, E> &x, const U &v) {
4234 return x.has_value() ? *x == v : false;
4235 }
4236 template <class T, class E, class U>
4237 constexpr bool operator==(const U &v, const expected<T, E> &x) {
4238 return x.has_value() ? *x == v : false;
4239 }
4240 template <class T, class E, class U>
4241 constexpr bool operator!=(const expected<T, E> &x, const U &v) {
4242 return x.has_value() ? *x != v : true;
4243 }
4244 template <class T, class E, class U>
4245 constexpr bool operator!=(const U &v, const expected<T, E> &x) {
4246 return x.has_value() ? *x != v : true;
4247 }
4248
4249 template <class T, class E>
4250 constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
4251 return x.has_value() ? false : x.error() == e.value();
4252 }
4253 template <class T, class E>
4254 constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
4255 return x.has_value() ? false : x.error() == e.value();
4256 }
4257 template <class T, class E>
4258 constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
4259 return x.has_value() ? true : x.error() != e.value();
4260 }
4261 template <class T, class E>
4262 constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
4263 return x.has_value() ? true : x.error() != e.value();
4264 }
4265
4266 template <class T, class E,
4267 detail::enable_if_t<(std::is_void<T>::value ||
4268 std::is_move_constructible<T>::value) &&
4269 detail::is_swappable<T>::value &&
4270 std::is_move_constructible<E>::value &&
4271 detail::is_swappable<E>::value> * = nullptr>
4272 void swap(expected<T, E> &lhs,
4273 expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
4274 lhs.swap(rhs);
4275 }
4276 } // namespace tl
4277
4278 #endif
4279 /* end file include/ada/expected.h */
4280
4281 #include <optional>
4282 #include <string_view>
4283
4284 /**
4285 * @private
4286 */
4287 namespace ada {
4288 struct url_aggregator;
4289 struct url;
4290 } // namespace ada
4291
4292 /**
4293 * @namespace ada::parser
4294 * @brief Includes the definitions for supported parsers
4295 */
4296 namespace ada::parser {
4297
4298 /**
4299 * Parses a url.
4300 */
4301 template <typename result_type = ada::url_aggregator>
4302 result_type parse_url(std::string_view user_input,
4303 const result_type* base_url = nullptr);
4304
4305 extern template url_aggregator parse_url<url_aggregator>(
4306 std::string_view user_input, const url_aggregator* base_url);
4307 extern template url parse_url<url>(std::string_view user_input,
4308 const url* base_url);
4309
4310 } // namespace ada::parser
4311
4312 #endif // ADA_PARSER_H
4313 /* end file include/ada/parser.h */
4314 /* begin file include/ada/scheme-inl.h */
4315 /**
4316 * @file scheme-inl.h
4317 * @brief Definitions for the URL scheme.
4318 */
4319 #ifndef ADA_SCHEME_INL_H
4320 #define ADA_SCHEME_INL_H
4321
4322
4323 namespace ada::scheme {
4324
4325 /**
4326 * @namespace ada::scheme::details
4327 * @brief Includes the definitions for scheme specific entities
4328 */
4329 namespace details {
4330 // for use with is_special and get_special_port
4331 // Spaces, if present, are removed from URL.
4332 constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws",
4333 "ftp", "wss", "file", " "};
4334 // for use with get_special_port
4335 constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
4336 } // namespace details
4337
4338 ada_really_inline constexpr bool is_special(std::string_view scheme) {
4339 if (scheme.empty()) {
4340 return false;
4341 }
4342 int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
4343 const std::string_view target = details::is_special_list[hash_value];
4344 return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
4345 }
4346 constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
4347 if (scheme.empty()) {
4348 return 0;
4349 }
4350 int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
4351 const std::string_view target = details::is_special_list[hash_value];
4352 if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) {
4353 return details::special_ports[hash_value];
4354 } else {
4355 return 0;
4356 }
4357 }
4358 constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
4359 return details::special_ports[int(type)];
4360 }
4361 constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
4362 if (scheme.empty()) {
4363 return ada::scheme::NOT_SPECIAL;
4364 }
4365 int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
4366 const std::string_view target = details::is_special_list[hash_value];
4367 if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) {
4368 return ada::scheme::type(hash_value);
4369 } else {
4370 return ada::scheme::NOT_SPECIAL;
4371 }
4372 }
4373
4374 } // namespace ada::scheme
4375
4376 #endif // ADA_SCHEME_INL_H
4377 /* end file include/ada/scheme-inl.h */
4378 /* begin file include/ada/serializers.h */
4379 /**
4380 * @file serializers.h
4381 * @brief Definitions for the URL serializers.
4382 */
4383 #ifndef ADA_SERIALIZERS_H
4384 #define ADA_SERIALIZERS_H
4385
4386
4387 #include <array>
4388 #include <optional>
4389 #include <string>
4390
4391 /**
4392 * @namespace ada::serializers
4393 * @brief Includes the definitions for URL serializers
4394 */
4395 namespace ada::serializers {
4396
4397 /**
4398 * Finds and returns the longest sequence of 0 values in a ipv6 input.
4399 */
4400 void find_longest_sequence_of_ipv6_pieces(
4401 const std::array<uint16_t, 8>& address, size_t& compress,
4402 size_t& compress_length) noexcept;
4403
4404 /**
4405 * Serializes an ipv6 address.
4406 * @details An IPv6 address is a 128-bit unsigned integer that identifies a
4407 * network address.
4408 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
4409 */
4410 std::string ipv6(const std::array<uint16_t, 8>& address) noexcept;
4411
4412 /**
4413 * Serializes an ipv4 address.
4414 * @details An IPv4 address is a 32-bit unsigned integer that identifies a
4415 * network address.
4416 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
4417 */
4418 std::string ipv4(uint64_t address) noexcept;
4419
4420 } // namespace ada::serializers
4421
4422 #endif // ADA_SERIALIZERS_H
4423 /* end file include/ada/serializers.h */
4424 /* begin file include/ada/unicode.h */
4425 /**
4426 * @file unicode.h
4427 * @brief Definitions for all unicode specific functions.
4428 */
4429 #ifndef ADA_UNICODE_H
4430 #define ADA_UNICODE_H
4431
4432
4433 #include <string>
4434 #include <optional>
4435
4436 /**
4437 * @namespace ada::unicode
4438 * @brief Includes the definitions for unicode operations
4439 */
4440 namespace ada::unicode {
4441
4442 /**
4443 * We receive a UTF-8 string representing a domain name.
4444 * If the string is percent encoded, we apply percent decoding.
4445 *
4446 * Given a domain, we need to identify its labels.
4447 * They are separated by label-separators:
4448 *
4449 * U+002E (.) FULL STOP
4450 * U+FF0E FULLWIDTH FULL STOP
4451 * U+3002 IDEOGRAPHIC FULL STOP
4452 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
4453 *
4454 * They are all mapped to U+002E.
4455 *
4456 * We process each label into a string that should not exceed 63 octets.
4457 * If the string is already punycode (starts with "xn--"), then we must
4458 * scan it to look for unallowed code points.
4459 * Otherwise, if the string is not pure ASCII, we need to transcode it
4460 * to punycode by following RFC 3454 which requires us to
4461 * - Map characters (see section 3),
4462 * - Normalize (see section 4),
4463 * - Reject forbidden characters,
4464 * - Check for right-to-left characters and if so, check all requirements (see
4465 * section 6),
4466 * - Optionally reject based on unassigned code points (section 7).
4467 *
4468 * The Unicode standard provides a table of code points with a mapping, a list
4469 * of forbidden code points and so forth. This table is subject to change and
4470 * will vary based on the implementation. For Unicode 15, the table is at
4471 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
4472 * If you use ICU, they parse this table and map it to code using a Python
4473 * script.
4474 *
4475 * The resulting strings should not exceed 255 octets according to RFC 1035
4476 * section 2.3.4. ICU checks for label size and domain size, but these errors
4477 * are ignored.
4478 *
4479 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
4480 *
4481 */
4482 bool to_ascii(std::optional<std::string>& out, std::string_view plain,
4483 size_t first_percent);
4484
4485 /**
4486 * @see https://www.unicode.org/reports/tr46/#ToUnicode
4487 */
4488 std::string to_unicode(std::string_view input);
4489
4490 /**
4491 * Checks if the input has tab or newline characters.
4492 *
4493 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
4494 * enough that compilers like GCC can 'autovectorize it'.
4495 */
4496 ada_really_inline bool has_tabs_or_newline(
4497 std::string_view user_input) noexcept;
4498
4499 /**
4500 * Checks if the input is a forbidden host code point.
4501 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
4502 */
4503 ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
4504
4505 /**
4506 * Checks if the input contains a forbidden domain code point.
4507 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
4508 */
4509 ada_really_inline constexpr bool contains_forbidden_domain_code_point(
4510 const char* input, size_t length) noexcept;
4511
4512 /**
4513 * Checks if the input contains a forbidden domain code point in which case
4514 * the first bit is set to 1. If the input contains an upper case ASCII letter,
4515 * then the second bit is set to 1.
4516 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
4517 */
4518 ada_really_inline constexpr uint8_t
4519 contains_forbidden_domain_code_point_or_upper(const char* input,
4520 size_t length) noexcept;
4521
4522 /**
4523 * Checks if the input is a forbidden domain code point.
4524 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
4525 */
4526 ada_really_inline constexpr bool is_forbidden_domain_code_point(
4527 char c) noexcept;
4528
4529 /**
4530 * Checks if the input is alphanumeric, '+', '-' or '.'
4531 */
4532 ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
4533
4534 /**
4535 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
4536 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
4537 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
4538 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
4539 */
4540 ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
4541
4542 /**
4543 * Checks if the input is a C0 control or space character.
4544 *
4545 * @details A C0 control or space is a C0 control or U+0020 SPACE.
4546 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
4547 * SEPARATOR ONE, inclusive.
4548 */
4549 ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
4550
4551 /**
4552 * Checks if the input is a ASCII tab or newline character.
4553 *
4554 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
4555 */
4556 ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
4557
4558 /**
4559 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
4560 * match for ".%2e", "%2e.", or "%2e%2e".
4561 */
4562 ada_really_inline ada_constexpr bool is_double_dot_path_segment(
4563 std::string_view input) noexcept;
4564
4565 /**
4566 * @details A single-dot path segment must be "." or an ASCII case-insensitive
4567 * match for "%2e".
4568 */
4569 ada_really_inline constexpr bool is_single_dot_path_segment(
4570 std::string_view input) noexcept;
4571
4572 /**
4573 * @details ipv4 character might contain 0-9 or a-f character ranges.
4574 */
4575 ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
4576
4577 /**
4578 * @details Convert hex to binary. Caller is responsible to ensure that
4579 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
4580 */
4581 ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
4582
4583 /**
4584 * first_percent should be = input.find('%')
4585 *
4586 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
4587 * @author Node.js
4588 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
4589 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
4590 */
4591 std::string percent_decode(std::string_view input, size_t first_percent);
4592
4593 /**
4594 * Returns a percent-encoding string whether percent encoding was needed or not.
4595 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
4596 */
4597 std::string percent_encode(std::string_view input,
4598 const uint8_t character_set[]);
4599 /**
4600 * Returns a percent-encoded string version of input, while starting the percent
4601 * encoding at the provided index.
4602 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
4603 */
4604 std::string percent_encode(std::string_view input,
4605 const uint8_t character_set[], size_t index);
4606 /**
4607 * Returns true if percent encoding was needed, in which case, we store
4608 * the percent-encoded content in 'out'. If the boolean 'append' is set to
4609 * true, the content is appended to 'out'.
4610 * If percent encoding is not needed, out is left unchanged.
4611 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
4612 */
4613 template <bool append>
4614 bool percent_encode(std::string_view input, const uint8_t character_set[],
4615 std::string& out);
4616 /**
4617 * Returns the index at which percent encoding should start, or (equivalently),
4618 * the length of the prefix that does not require percent encoding.
4619 */
4620 ada_really_inline size_t percent_encode_index(std::string_view input,
4621 const uint8_t character_set[]);
4622 /**
4623 * Lowers the string in-place, assuming that the content is ASCII.
4624 * Return true if the content was ASCII.
4625 */
4626 constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
4627 } // namespace ada::unicode
4628
4629 #endif // ADA_UNICODE_H
4630 /* end file include/ada/unicode.h */
4631 /* begin file include/ada/url_base-inl.h */
4632 /**
4633 * @file url_base-inl.h
4634 * @brief Inline functions for url base
4635 */
4636 #ifndef ADA_URL_BASE_INL_H
4637 #define ADA_URL_BASE_INL_H
4638
4639 /* begin file include/ada/url_aggregator.h */
4640 /**
4641 * @file url_aggregator.h
4642 * @brief Declaration for the basic URL definitions
4643 */
4644 #ifndef ADA_URL_AGGREGATOR_H
4645 #define ADA_URL_AGGREGATOR_H
4646
4647
4648 #include <string>
4649 #include <string_view>
4650
4651 namespace ada {
4652
4653 /**
4654 * @brief Lightweight URL struct.
4655 *
4656 * @details The url_aggregator class aims to minimize temporary memory
4657 * allocation while representing a parsed URL. Internally, it contains a single
4658 * normalized URL (the href), and it makes available the components, mostly
4659 * using std::string_view.
4660 */
4661 struct url_aggregator : url_base {
4662 url_aggregator() = default;
4663 url_aggregator(const url_aggregator &u) = default;
4664 url_aggregator(url_aggregator &&u) noexcept = default;
4665 url_aggregator &operator=(url_aggregator &&u) noexcept = default;
4666 url_aggregator &operator=(const url_aggregator &u) = default;
4667 ~url_aggregator() override = default;
4668
4669 bool set_href(std::string_view input);
4670 bool set_host(std::string_view input);
4671 bool set_hostname(std::string_view input);
4672 bool set_protocol(std::string_view input);
4673 bool set_username(std::string_view input);
4674 bool set_password(std::string_view input);
4675 bool set_port(std::string_view input);
4676 bool set_pathname(std::string_view input);
4677 void set_search(std::string_view input);
4678 void set_hash(std::string_view input);
4679
4680 [[nodiscard]] bool has_valid_domain() const noexcept override;
4681 /**
4682 * The origin getter steps are to return the serialization of this's URL's
4683 * origin. [HTML]
4684 * @return a newly allocated string.
4685 * @see https://url.spec.whatwg.org/#concept-url-origin
4686 */
4687 [[nodiscard]] std::string get_origin() const noexcept override;
4688 /**
4689 * Return the normalized string.
4690 * This function does not allocate memory.
4691 * It is highly efficient.
4692 * @return a constant reference to the underlying normalized URL.
4693 * @see https://url.spec.whatwg.org/#dom-url-href
4694 * @see https://url.spec.whatwg.org/#concept-url-serializer
4695 */
4696 [[nodiscard]] inline std::string_view get_href() const noexcept;
4697 /**
4698 * The username getter steps are to return this's URL's username.
4699 * This function does not allocate memory.
4700 * @return a lightweight std::string_view.
4701 * @see https://url.spec.whatwg.org/#dom-url-username
4702 */
4703 [[nodiscard]] std::string_view get_username() const noexcept;
4704 /**
4705 * The password getter steps are to return this's URL's password.
4706 * This function does not allocate memory.
4707 * @return a lightweight std::string_view.
4708 * @see https://url.spec.whatwg.org/#dom-url-password
4709 */
4710 [[nodiscard]] std::string_view get_password() const noexcept;
4711 /**
4712 * Return this's URL's port, serialized.
4713 * This function does not allocate memory.
4714 * @return a lightweight std::string_view.
4715 * @see https://url.spec.whatwg.org/#dom-url-port
4716 */
4717 [[nodiscard]] std::string_view get_port() const noexcept;
4718 /**
4719 * Return U+0023 (#), followed by this's URL's fragment.
4720 * This function does not allocate memory.
4721 * @return a lightweight std::string_view..
4722 * @see https://url.spec.whatwg.org/#dom-url-hash
4723 */
4724 [[nodiscard]] std::string_view get_hash() const noexcept;
4725 /**
4726 * Return url's host, serialized, followed by U+003A (:) and url's port,
4727 * serialized.
4728 * This function does not allocate memory.
4729 * When there is no host, this function returns the empty view.
4730 * @return a lightweight std::string_view.
4731 * @see https://url.spec.whatwg.org/#dom-url-host
4732 */
4733 [[nodiscard]] std::string_view get_host() const noexcept;
4734 /**
4735 * Return this's URL's host, serialized.
4736 * This function does not allocate memory.
4737 * When there is no host, this function returns the empty view.
4738 * @return a lightweight std::string_view.
4739 * @see https://url.spec.whatwg.org/#dom-url-hostname
4740 */
4741 [[nodiscard]] std::string_view get_hostname() const noexcept;
4742 /**
4743 * The pathname getter steps are to return the result of URL path serializing
4744 * this's URL.
4745 * This function does not allocate memory.
4746 * @return a lightweight std::string_view.
4747 * @see https://url.spec.whatwg.org/#dom-url-pathname
4748 */
4749 [[nodiscard]] std::string_view get_pathname() const noexcept;
4750 /**
4751 * Compute the pathname length in bytes without instantiating a view or a
4752 * string.
4753 * @return size of the pathname in bytes
4754 * @see https://url.spec.whatwg.org/#dom-url-pathname
4755 */
4756 [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
4757 /**
4758 * Return U+003F (?), followed by this's URL's query.
4759 * This function does not allocate memory.
4760 * @return a lightweight std::string_view.
4761 * @see https://url.spec.whatwg.org/#dom-url-search
4762 */
4763 [[nodiscard]] std::string_view get_search() const noexcept;
4764 /**
4765 * The protocol getter steps are to return this's URL's scheme, followed by
4766 * U+003A (:).
4767 * This function does not allocate memory.
4768 * @return a lightweight std::string_view.
4769 * @see https://url.spec.whatwg.org/#dom-url-protocol
4770 */
4771 [[nodiscard]] std::string_view get_protocol() const noexcept;
4772
4773 /**
4774 * A URL includes credentials if its username or password is not the empty
4775 * string.
4776 */
4777 [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
4778
4779 /**
4780 * Useful for implementing efficient serialization for the URL.
4781 *
4782 * https://user:pass@example.com:1234/foo/bar?baz#quux
4783 * | | | | ^^^^| | |
4784 * | | | | | | | `----- hash_start
4785 * | | | | | | `--------- search_start
4786 * | | | | | `----------------- pathname_start
4787 * | | | | `--------------------- port
4788 * | | | `----------------------- host_end
4789 * | | `---------------------------------- host_start
4790 * | `--------------------------------------- username_end
4791 * `--------------------------------------------- protocol_end
4792 *
4793 * Inspired after servo/url
4794 *
4795 * @return a constant reference to the underlying component attribute.
4796 *
4797 * @see
4798 * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31
4799 */
4800 [[nodiscard]] ada_really_inline const ada::url_components &get_components()
4801 const noexcept;
4802 /**
4803 * Returns a string representation of this URL.
4804 */
4805 [[nodiscard]] std::string to_string() const override;
4806 /**
4807 * Returns a string diagram of this URL.
4808 */
4809 [[nodiscard]] std::string to_diagram() const;
4810
4811 /**
4812 * Verifies that the parsed URL could be valid. Useful for debugging purposes.
4813 * @return true if the URL is valid, otherwise return true of the offsets are
4814 * possible.
4815 */
4816 [[nodiscard]] bool validate() const noexcept;
4817
4818 /** @return true if it has an host but it is the empty string */
4819 [[nodiscard]] inline bool has_empty_hostname() const noexcept;
4820 /** @return true if it has a host (included an empty host) */
4821 [[nodiscard]] inline bool has_hostname() const noexcept;
4822 /** @return true if the URL has a non-empty username */
4823 [[nodiscard]] inline bool has_non_empty_username() const noexcept;
4824 /** @return true if the URL has a non-empty password */
4825 [[nodiscard]] inline bool has_non_empty_password() const noexcept;
4826 /** @return true if the URL has a (non default) port */
4827 [[nodiscard]] inline bool has_port() const noexcept;
4828 /** @return true if the URL has a password */
4829 [[nodiscard]] inline bool has_password() const noexcept;
4830 /** @return true if the URL has a hash component */
4831 [[nodiscard]] inline bool has_hash() const noexcept override;
4832 /** @return true if the URL has a search component */
4833 [[nodiscard]] inline bool has_search() const noexcept override;
4834
4835 inline void clear_port();
4836 inline void clear_hash();
4837 inline void clear_search() override;
4838
4839 private:
4840 friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
4841 std::string_view, const ada::url_aggregator *);
4842 friend void ada::helpers::strip_trailing_spaces_from_opaque_path<
4843 ada::url_aggregator>(ada::url_aggregator &url) noexcept;
4844
4845 std::string buffer{};
4846 url_components components{};
4847
4848 /**
4849 * Returns true if neither the search, nor the hash nor the pathname
4850 * have been set.
4851 * @return true if the buffer is ready to receive the path.
4852 */
4853 [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
4854
4855 inline void add_authority_slashes_if_needed() noexcept;
4856
4857 /**
4858 * To optimize performance, you may indicate how much memory to allocate
4859 * within this instance.
4860 */
4861 inline void reserve(uint32_t capacity);
4862
4863 ada_really_inline size_t parse_port(
4864 std::string_view view, bool check_trailing_content) noexcept override;
4865
4866 ada_really_inline size_t parse_port(std::string_view view) noexcept override {
4867 return this->parse_port(view, false);
4868 }
4869
4870 /**
4871 * Return true on success. The 'in_place' parameter indicates whether the
4872 * the string_view input is pointing in the buffer. When in_place is false,
4873 * we must nearly always update the buffer.
4874 * @see https://url.spec.whatwg.org/#concept-ipv4-parser
4875 */
4876 [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
4877
4878 /**
4879 * Return true on success.
4880 * @see https://url.spec.whatwg.org/#concept-ipv6-parser
4881 */
4882 [[nodiscard]] bool parse_ipv6(std::string_view input);
4883
4884 /**
4885 * Return true on success.
4886 * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
4887 */
4888 [[nodiscard]] bool parse_opaque_host(std::string_view input);
4889
4890 ada_really_inline void parse_path(std::string_view input);
4891
4892 /**
4893 * A URL cannot have a username/password/port if its host is null or the empty
4894 * string, or its scheme is "file".
4895 */
4896 [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
4897
4898 template <bool override_hostname = false>
4899 bool set_host_or_hostname(std::string_view input);
4900
4901 ada_really_inline bool parse_host(std::string_view input);
4902
4903 inline void update_base_authority(std::string_view base_buffer,
4904 const ada::url_components &base);
4905 inline void update_unencoded_base_hash(std::string_view input);
4906 inline void update_base_hostname(std::string_view input);
4907 inline void update_base_search(std::string_view input);
4908 inline void update_base_search(std::string_view input,
4909 const uint8_t *query_percent_encode_set);
4910 inline void update_base_pathname(std::string_view input);
4911 inline void update_base_username(std::string_view input);
4912 inline void append_base_username(std::string_view input);
4913 inline void update_base_password(std::string_view input);
4914 inline void append_base_password(std::string_view input);
4915 inline void update_base_port(uint32_t input);
4916 inline void append_base_pathname(std::string_view input);
4917 [[nodiscard]] inline uint32_t retrieve_base_port() const;
4918 inline void clear_hostname();
4919 inline void clear_password();
4920 inline void clear_pathname() override;
4921 [[nodiscard]] inline bool has_dash_dot() const noexcept;
4922 void delete_dash_dot();
4923 inline void consume_prepared_path(std::string_view input);
4924 template <bool has_state_override = false>
4925 [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
4926 std::string_view input);
4927 ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
4928 std::string_view input);
4929 [[nodiscard]] inline bool has_authority() const noexcept;
4930 inline void set_protocol_as_file();
4931 inline void set_scheme(std::string_view new_scheme) noexcept;
4932 /**
4933 * Fast function to set the scheme from a view with a colon in the
4934 * buffer, does not change type.
4935 */
4936 inline void set_scheme_from_view_with_colon(
4937 std::string_view new_scheme_with_colon) noexcept;
4938 inline void copy_scheme(const url_aggregator &u) noexcept;
4939
4940 }; // url_aggregator
4941
4942 inline std::ostream &operator<<(std::ostream &out, const ada::url &u);
4943 } // namespace ada
4944
4945 #endif
4946 /* end file include/ada/url_aggregator.h */
4947 /* begin file include/ada/checkers.h */
4948 /**
4949 * @file checkers.h
4950 * @brief Declarations for URL specific checkers used within Ada.
4951 */
4952 #ifndef ADA_CHECKERS_H
4953 #define ADA_CHECKERS_H
4954
4955
4956 #include <string_view>
4957 #include <cstring>
4958
4959 /**
4960 * @namespace ada::checkers
4961 * @brief Includes the definitions for validation functions
4962 */
4963 namespace ada::checkers {
4964
4965 /**
4966 * Assuming that x is an ASCII letter, this function returns the lower case
4967 * equivalent.
4968 * @details More likely to be inlined by the compiler and constexpr.
4969 */
4970 constexpr char to_lower(char x) noexcept;
4971
4972 /**
4973 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
4974 * but more likely to be inlined by the compiler.
4975 *
4976 * @attention std::isalpha is not constexpr generally.
4977 */
4978 constexpr bool is_alpha(char x) noexcept;
4979
4980 /**
4981 * Check whether a string starts with 0x or 0X. The function is only
4982 * safe if input.size() >=2.
4983 *
4984 * @see has_hex_prefix
4985 */
4986 inline bool has_hex_prefix_unsafe(std::string_view input);
4987 /**
4988 * Check whether a string starts with 0x or 0X.
4989 */
4990 inline bool has_hex_prefix(std::string_view input);
4991
4992 /**
4993 * Check whether x is an ASCII digit. More likely to be inlined than
4994 * std::isdigit.
4995 */
4996 constexpr bool is_digit(char x) noexcept;
4997
4998 /**
4999 * @details A string starts with a Windows drive letter if all of the following
5000 * are true:
5001 *
5002 * - its length is greater than or equal to 2
5003 * - its first two code points are a Windows drive letter
5004 * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
5005 * (?), or U+0023 (#).
5006 *
5007 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
5008 */
5009 inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
5010
5011 /**
5012 * @details A normalized Windows drive letter is a Windows drive letter of which
5013 * the second code point is U+003A (:).
5014 */
5015 inline constexpr bool is_normalized_windows_drive_letter(
5016 std::string_view input) noexcept;
5017
5018 /**
5019 * @warning Will be removed when Ada requires C++20.
5020 */
5021 ada_really_inline bool begins_with(std::string_view view,
5022 std::string_view prefix);
5023
5024 /**
5025 * Returns true if an input is an ipv4 address. It is assumed that the string
5026 * does not contain uppercase ASCII characters (the input should have been
5027 * lowered cased before calling this function) and is not empty.
5028 */
5029 ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept;
5030
5031 /**
5032 * Returns a bitset. If the first bit is set, then at least one character needs
5033 * percent encoding. If the second bit is set, a \\ is found. If the third bit
5034 * is set then we have a dot. If the fourth bit is set, then we have a percent
5035 * character.
5036 */
5037 ada_really_inline constexpr uint8_t path_signature(
5038 std::string_view input) noexcept;
5039
5040 /**
5041 * Returns true if the length of the domain name and its labels are according to
5042 * the specifications. The length of the domain must be 255 octets (253
5043 * characters not including the last 2 which are the empty label reserved at the
5044 * end). When the empty label is included (a dot at the end), the domain name
5045 * can have 254 characters. The length of a label must be at least 1 and at most
5046 * 63 characters.
5047 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
5048 * @see https://www.unicode.org/reports/tr46/#ToASCII
5049 */
5050 ada_really_inline constexpr bool verify_dns_length(
5051 std::string_view input) noexcept;
5052
5053 } // namespace ada::checkers
5054
5055 #endif // ADA_CHECKERS_H
5056 /* end file include/ada/checkers.h */
5057 /* begin file include/ada/url.h */
5058 /**
5059 * @file url.h
5060 * @brief Declaration for the URL
5061 */
5062 #ifndef ADA_URL_H
5063 #define ADA_URL_H
5064
5065
5066 #include <algorithm>
5067 #include <charconv>
5068 #include <iostream>
5069 #include <optional>
5070 #include <string>
5071 #include <string_view>
5072
5073 namespace ada {
5074
5075 /**
5076 * @brief Generic URL struct reliant on std::string instantiation.
5077 *
5078 * @details To disambiguate from a valid URL string it can also be referred to
5079 * as a URL record. A URL is a struct that represents a universal identifier.
5080 * Unlike the url_aggregator, the ada::url represents the different components
5081 * of a parsed URL as independent std::string instances. This makes the
5082 * structure heavier and more reliant on memory allocations. When getting
5083 * components from the parsed URL, a new std::string is typically constructed.
5084 *
5085 * @see https://url.spec.whatwg.org/#url-representation
5086 */
5087 struct url : url_base {
5088 url() = default;
5089 url(const url &u) = default;
5090 url(url &&u) noexcept = default;
5091 url &operator=(url &&u) noexcept = default;
5092 url &operator=(const url &u) = default;
5093 ~url() override = default;
5094
5095 /**
5096 * @private
5097 * A URL's username is an ASCII string identifying a username. It is initially
5098 * the empty string.
5099 */
5100 std::string username{};
5101
5102 /**
5103 * @private
5104 * A URL's password is an ASCII string identifying a password. It is initially
5105 * the empty string.
5106 */
5107 std::string password{};
5108
5109 /**
5110 * @private
5111 * A URL's host is null or a host. It is initially null.
5112 */
5113 std::optional<std::string> host{};
5114
5115 /**
5116 * @private
5117 * A URL's port is either null or a 16-bit unsigned integer that identifies a
5118 * networking port. It is initially null.
5119 */
5120 std::optional<uint16_t> port{};
5121
5122 /**
5123 * @private
5124 * A URL's path is either an ASCII string or a list of zero or more ASCII
5125 * strings, usually identifying a location.
5126 */
5127 std::string path{};
5128
5129 /**
5130 * @private
5131 * A URL's query is either null or an ASCII string. It is initially null.
5132 */
5133 std::optional<std::string> query{};
5134
5135 /**
5136 * @private
5137 * A URL's fragment is either null or an ASCII string that can be used for
5138 * further processing on the resource the URL's other components identify. It
5139 * is initially null.
5140 */
5141 std::optional<std::string> hash{};
5142
5143 /** @return true if it has an host but it is the empty string */
5144 [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5145 /** @return true if the URL has a (non default) port */
5146 [[nodiscard]] inline bool has_port() const noexcept;
5147 /** @return true if it has a host (included an empty host) */
5148 [[nodiscard]] inline bool has_hostname() const noexcept;
5149 [[nodiscard]] bool has_valid_domain() const noexcept override;
5150
5151 /**
5152 * Returns a JSON string representation of this URL.
5153 */
5154 [[nodiscard]] std::string to_string() const override;
5155
5156 /**
5157 * @see https://url.spec.whatwg.org/#dom-url-href
5158 * @see https://url.spec.whatwg.org/#concept-url-serializer
5159 */
5160 [[nodiscard]] ada_really_inline std::string get_href() const noexcept;
5161
5162 /**
5163 * The origin getter steps are to return the serialization of this's URL's
5164 * origin. [HTML]
5165 * @return a newly allocated string.
5166 * @see https://url.spec.whatwg.org/#concept-url-origin
5167 */
5168 [[nodiscard]] std::string get_origin() const noexcept override;
5169
5170 /**
5171 * The protocol getter steps are to return this's URL's scheme, followed by
5172 * U+003A (:).
5173 * @return a newly allocated string.
5174 * @see https://url.spec.whatwg.org/#dom-url-protocol
5175 */
5176 [[nodiscard]] std::string get_protocol() const noexcept;
5177
5178 /**
5179 * Return url's host, serialized, followed by U+003A (:) and url's port,
5180 * serialized.
5181 * When there is no host, this function returns the empty string.
5182 * @return a newly allocated string.
5183 * @see https://url.spec.whatwg.org/#dom-url-host
5184 */
5185 [[nodiscard]] std::string get_host() const noexcept;
5186
5187 /**
5188 * Return this's URL's host, serialized.
5189 * When there is no host, this function returns the empty string.
5190 * @return a newly allocated string.
5191 * @see https://url.spec.whatwg.org/#dom-url-hostname
5192 */
5193 [[nodiscard]] std::string get_hostname() const noexcept;
5194
5195 /**
5196 * The pathname getter steps are to return the result of URL path serializing
5197 * this's URL.
5198 * @return a newly allocated string.
5199 * @see https://url.spec.whatwg.org/#dom-url-pathname
5200 */
5201 [[nodiscard]] std::string_view get_pathname() const noexcept;
5202
5203 /**
5204 * Compute the pathname length in bytes without instantiating a view or a
5205 * string.
5206 * @return size of the pathname in bytes
5207 * @see https://url.spec.whatwg.org/#dom-url-pathname
5208 */
5209 [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5210
5211 /**
5212 * Return U+003F (?), followed by this's URL's query.
5213 * @return a newly allocated string.
5214 * @see https://url.spec.whatwg.org/#dom-url-search
5215 */
5216 [[nodiscard]] std::string get_search() const noexcept;
5217
5218 /**
5219 * The username getter steps are to return this's URL's username.
5220 * @return a constant reference to the underlying string.
5221 * @see https://url.spec.whatwg.org/#dom-url-username
5222 */
5223 [[nodiscard]] const std::string &get_username() const noexcept;
5224
5225 /**
5226 * @return Returns true on successful operation.
5227 * @see https://url.spec.whatwg.org/#dom-url-username
5228 */
5229 bool set_username(std::string_view input);
5230
5231 /**
5232 * @return Returns true on success.
5233 * @see https://url.spec.whatwg.org/#dom-url-password
5234 */
5235 bool set_password(std::string_view input);
5236
5237 /**
5238 * @return Returns true on success.
5239 * @see https://url.spec.whatwg.org/#dom-url-port
5240 */
5241 bool set_port(std::string_view input);
5242
5243 /**
5244 * This function always succeeds.
5245 * @see https://url.spec.whatwg.org/#dom-url-hash
5246 */
5247 void set_hash(std::string_view input);
5248
5249 /**
5250 * This function always succeeds.
5251 * @see https://url.spec.whatwg.org/#dom-url-search
5252 */
5253 void set_search(std::string_view input);
5254
5255 /**
5256 * @return Returns true on success.
5257 * @see https://url.spec.whatwg.org/#dom-url-search
5258 */
5259 bool set_pathname(std::string_view input);
5260
5261 /**
5262 * @return Returns true on success.
5263 * @see https://url.spec.whatwg.org/#dom-url-host
5264 */
5265 bool set_host(std::string_view input);
5266
5267 /**
5268 * @return Returns true on success.
5269 * @see https://url.spec.whatwg.org/#dom-url-hostname
5270 */
5271 bool set_hostname(std::string_view input);
5272
5273 /**
5274 * @return Returns true on success.
5275 * @see https://url.spec.whatwg.org/#dom-url-protocol
5276 */
5277 bool set_protocol(std::string_view input);
5278
5279 /**
5280 * @see https://url.spec.whatwg.org/#dom-url-href
5281 */
5282 bool set_href(std::string_view input);
5283
5284 /**
5285 * The password getter steps are to return this's URL's password.
5286 * @return a constant reference to the underlying string.
5287 * @see https://url.spec.whatwg.org/#dom-url-password
5288 */
5289 [[nodiscard]] const std::string &get_password() const noexcept;
5290
5291 /**
5292 * Return this's URL's port, serialized.
5293 * @return a newly constructed string representing the port.
5294 * @see https://url.spec.whatwg.org/#dom-url-port
5295 */
5296 [[nodiscard]] std::string get_port() const noexcept;
5297
5298 /**
5299 * Return U+0023 (#), followed by this's URL's fragment.
5300 * @return a newly constructed string representing the hash.
5301 * @see https://url.spec.whatwg.org/#dom-url-hash
5302 */
5303 [[nodiscard]] std::string get_hash() const noexcept;
5304
5305 /**
5306 * A URL includes credentials if its username or password is not the empty
5307 * string.
5308 */
5309 [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5310
5311 /**
5312 * Useful for implementing efficient serialization for the URL.
5313 *
5314 * https://user:pass@example.com:1234/foo/bar?baz#quux
5315 * | | | | ^^^^| | |
5316 * | | | | | | | `----- hash_start
5317 * | | | | | | `--------- search_start
5318 * | | | | | `----------------- pathname_start
5319 * | | | | `--------------------- port
5320 * | | | `----------------------- host_end
5321 * | | `---------------------------------- host_start
5322 * | `--------------------------------------- username_end
5323 * `--------------------------------------------- protocol_end
5324 *
5325 * Inspired after servo/url
5326 *
5327 * @return a newly constructed component.
5328 *
5329 * @see
5330 * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31
5331 */
5332 [[nodiscard]] ada_really_inline ada::url_components get_components()
5333 const noexcept;
5334 /** @return true if the URL has a hash component */
5335 [[nodiscard]] inline bool has_hash() const noexcept override;
5336 /** @return true if the URL has a search component */
5337 [[nodiscard]] inline bool has_search() const noexcept override;
5338
5339 private:
5340 friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5341 const ada::url *);
5342 friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5343 std::string_view, const ada::url_aggregator *);
5344 friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5345 ada::url &url) noexcept;
5346
5347 inline void update_unencoded_base_hash(std::string_view input);
5348 inline void update_base_hostname(std::string_view input);
5349 inline void update_base_search(std::string_view input);
5350 inline void update_base_search(std::string_view input,
5351 const uint8_t query_percent_encode_set[]);
5352 inline void update_base_search(std::optional<std::string> input);
5353 inline void update_base_pathname(std::string_view input);
5354 inline void update_base_username(std::string_view input);
5355 inline void update_base_password(std::string_view input);
5356 inline void update_base_port(std::optional<uint16_t> input);
5357
5358 /**
5359 * Sets the host or hostname according to override condition.
5360 * Return true on success.
5361 * @see https://url.spec.whatwg.org/#hostname-state
5362 */
5363 template <bool override_hostname = false>
5364 bool set_host_or_hostname(std::string_view input);
5365
5366 /**
5367 * Return true on success.
5368 * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5369 */
5370 [[nodiscard]] bool parse_ipv4(std::string_view input);
5371
5372 /**
5373 * Return true on success.
5374 * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5375 */
5376 [[nodiscard]] bool parse_ipv6(std::string_view input);
5377
5378 /**
5379 * Return true on success.
5380 * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5381 */
5382 [[nodiscard]] bool parse_opaque_host(std::string_view input);
5383
5384 /**
5385 * A URL's scheme is an ASCII string that identifies the type of URL and can
5386 * be used to dispatch a URL for further processing after parsing. It is
5387 * initially the empty string. We only set non_special_scheme when the scheme
5388 * is non-special, otherwise we avoid constructing string.
5389 *
5390 * Special schemes are stored in ada::scheme::details::is_special_list so we
5391 * typically do not need to store them in each url instance.
5392 */
5393 std::string non_special_scheme{};
5394
5395 /**
5396 * A URL cannot have a username/password/port if its host is null or the empty
5397 * string, or its scheme is "file".
5398 */
5399 [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5400
5401 ada_really_inline size_t parse_port(
5402 std::string_view view, bool check_trailing_content) noexcept override;
5403
5404 ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5405 return this->parse_port(view, false);
5406 }
5407
5408 /**
5409 * Take the scheme from another URL. The scheme string is copied from the
5410 * provided url.
5411 */
5412 inline void copy_scheme(const ada::url &u);
5413
5414 /**
5415 * Parse the host from the provided input. We assume that
5416 * the input does not contain spaces or tabs. Control
5417 * characters and spaces are not trimmed (they should have
5418 * been removed if needed).
5419 * Return true on success.
5420 * @see https://url.spec.whatwg.org/#host-parsing
5421 */
5422 [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5423
5424 template <bool has_state_override = false>
5425 [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5426
5427 inline void clear_pathname() override;
5428 inline void clear_search() override;
5429 inline void set_protocol_as_file();
5430
5431 /**
5432 * Parse the path from the provided input.
5433 * Return true on success. Control characters not
5434 * trimmed from the ends (they should have
5435 * been removed if needed).
5436 *
5437 * The input is expected to be UTF-8.
5438 *
5439 * @see https://url.spec.whatwg.org/
5440 */
5441 ada_really_inline void parse_path(std::string_view input);
5442
5443 /**
5444 * Set the scheme for this URL. The provided scheme should be a valid
5445 * scheme string, be lower-cased, not contain spaces or tabs. It should
5446 * have no spurious trailing or leading content.
5447 */
5448 inline void set_scheme(std::string &&new_scheme) noexcept;
5449
5450 /**
5451 * Take the scheme from another URL. The scheme string is moved from the
5452 * provided url.
5453 */
5454 inline void copy_scheme(ada::url &&u) noexcept;
5455
5456 }; // struct url
5457
5458 inline std::ostream &operator<<(std::ostream &out, const ada::url &u);
5459 } // namespace ada
5460
5461 #endif // ADA_URL_H
5462 /* end file include/ada/url.h */
5463
5464 #include <optional>
5465 #include <string>
5466 #if ADA_REGULAR_VISUAL_STUDIO
5467 #include <intrin.h>
5468 #endif // ADA_REGULAR_VISUAL_STUDIO
5469
5470 namespace ada {
5471
5472 [[nodiscard]] ada_really_inline bool url_base::is_special() const noexcept {
5473 return type != ada::scheme::NOT_SPECIAL;
5474 }
5475
5476 [[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
5477 return ada::scheme::get_special_port(type);
5478 }
5479
5480 [[nodiscard]] ada_really_inline uint16_t
5481 url_base::scheme_default_port() const noexcept {
5482 return scheme::get_special_port(type);
5483 }
5484
5485 } // namespace ada
5486
5487 #endif // ADA_URL_BASE_INL_H
5488 /* end file include/ada/url_base-inl.h */
5489 /* begin file include/ada/url-inl.h */
5490 /**
5491 * @file url-inl.h
5492 * @brief Definitions for the URL
5493 */
5494 #ifndef ADA_URL_INL_H
5495 #define ADA_URL_INL_H
5496
5497
5498 #include <optional>
5499 #include <string>
5500 #if ADA_REGULAR_VISUAL_STUDIO
5501 #include <intrin.h>
5502 #endif // ADA_REGULAR_VISUAL_STUDIO
5503
5504 namespace ada {
5505 [[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
5506 return !username.empty() || !password.empty();
5507 }
5508 [[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
5509 return port.has_value();
5510 }
5511 [[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
5512 return !host.has_value() || host.value().empty() ||
5513 type == ada::scheme::type::FILE;
5514 }
5515 [[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
5516 if (!host.has_value()) {
5517 return false;
5518 }
5519 return host.value().empty();
5520 }
5521 [[nodiscard]] inline bool url::has_hostname() const noexcept {
5522 return host.has_value();
5523 }
5524 inline std::ostream &operator<<(std::ostream &out, const ada::url &u) {
5525 return out << u.to_string();
5526 }
5527
5528 [[nodiscard]] size_t url::get_pathname_length() const noexcept {
5529 return path.size();
5530 }
5531
5532 [[nodiscard]] ada_really_inline ada::url_components url::get_components()
5533 const noexcept {
5534 url_components out{};
5535
5536 // protocol ends with ':'. for example: "https:"
5537 out.protocol_end = uint32_t(get_protocol().size());
5538
5539 // Trailing index is always the next character of the current one.
5540 size_t running_index = out.protocol_end;
5541
5542 if (host.has_value()) {
5543 // 2 characters for "//" and 1 character for starting index
5544 out.host_start = out.protocol_end + 2;
5545
5546 if (has_credentials()) {
5547 out.username_end = uint32_t(out.host_start + username.size());
5548
5549 out.host_start += uint32_t(username.size());
5550
5551 if (!password.empty()) {
5552 out.host_start += uint32_t(password.size() + 1);
5553 }
5554
5555 out.host_end = uint32_t(out.host_start + host.value().size());
5556 } else {
5557 out.username_end = out.host_start;
5558
5559 // Host does not start with "@" if it does not include credentials.
5560 out.host_end = uint32_t(out.host_start + host.value().size()) - 1;
5561 }
5562
5563 running_index = out.host_end + 1;
5564 } else {
5565 // Update host start and end date to the same index, since it does not
5566 // exist.
5567 out.host_start = out.protocol_end;
5568 out.host_end = out.host_start;
5569
5570 if (!has_opaque_path && checkers::begins_with(path, "//")) {
5571 // If url's host is null, url does not have an opaque path, url's path's
5572 // size is greater than 1, and url's path[0] is the empty string, then
5573 // append U+002F (/) followed by U+002E (.) to output.
5574 running_index = out.protocol_end + 2;
5575 } else {
5576 running_index = out.protocol_end;
5577 }
5578 }
5579
5580 if (port.has_value()) {
5581 out.port = *port;
5582 running_index += helpers::fast_digit_count(*port) + 1; // Port omits ':'
5583 }
5584
5585 out.pathname_start = uint32_t(running_index);
5586
5587 running_index += path.size();
5588
5589 if (query.has_value()) {
5590 out.search_start = uint32_t(running_index);
5591 running_index += get_search().size();
5592 if (get_search().size() == 0) {
5593 running_index++;
5594 }
5595 }
5596
5597 if (hash.has_value()) {
5598 out.hash_start = uint32_t(running_index);
5599 }
5600
5601 return out;
5602 }
5603
5604 inline void url::update_base_hostname(std::string_view input) { host = input; }
5605
5606 inline void url::update_unencoded_base_hash(std::string_view input) {
5607 // We do the percent encoding
5608 hash = unicode::percent_encode(input,
5609 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
5610 }
5611
5612 inline void url::update_base_search(std::string_view input,
5613 const uint8_t query_percent_encode_set[]) {
5614 query = ada::unicode::percent_encode(input, query_percent_encode_set);
5615 }
5616
5617 inline void url::update_base_search(std::optional<std::string> input) {
5618 query = input;
5619 }
5620
5621 inline void url::update_base_pathname(const std::string_view input) {
5622 path = input;
5623 }
5624
5625 inline void url::update_base_username(const std::string_view input) {
5626 username = input;
5627 }
5628
5629 inline void url::update_base_password(const std::string_view input) {
5630 password = input;
5631 }
5632
5633 inline void url::update_base_port(std::optional<uint16_t> input) {
5634 port = input;
5635 }
5636
5637 inline void url::clear_pathname() { path.clear(); }
5638
5639 inline void url::clear_search() { query = std::nullopt; }
5640
5641 [[nodiscard]] inline bool url::has_hash() const noexcept {
5642 return hash.has_value();
5643 }
5644
5645 [[nodiscard]] inline bool url::has_search() const noexcept {
5646 return query.has_value();
5647 }
5648
5649 inline void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
5650
5651 inline void url::set_scheme(std::string &&new_scheme) noexcept {
5652 type = ada::scheme::get_scheme_type(new_scheme);
5653 // We only move the 'scheme' if it is non-special.
5654 if (!is_special()) {
5655 non_special_scheme = new_scheme;
5656 }
5657 }
5658
5659 inline void url::copy_scheme(ada::url &&u) noexcept {
5660 non_special_scheme = u.non_special_scheme;
5661 type = u.type;
5662 }
5663
5664 inline void url::copy_scheme(const ada::url &u) {
5665 non_special_scheme = u.non_special_scheme;
5666 type = u.type;
5667 }
5668
5669 [[nodiscard]] ada_really_inline std::string url::get_href() const noexcept {
5670 std::string output = get_protocol();
5671
5672 if (host.has_value()) {
5673 output += "//";
5674 if (has_credentials()) {
5675 output += username;
5676 if (!password.empty()) {
5677 output += ":" + get_password();
5678 }
5679 output += "@";
5680 }
5681 output += host.value();
5682 if (port.has_value()) {
5683 output += ":" + get_port();
5684 }
5685 } else if (!has_opaque_path && checkers::begins_with(path, "//")) {
5686 // If url's host is null, url does not have an opaque path, url's path's
5687 // size is greater than 1, and url's path[0] is the empty string, then
5688 // append U+002F (/) followed by U+002E (.) to output.
5689 output += "/.";
5690 }
5691 output += path;
5692 if (query.has_value()) {
5693 output += "?" + query.value();
5694 }
5695 if (hash.has_value()) {
5696 output += "#" + hash.value();
5697 }
5698 return output;
5699 }
5700
5701 ada_really_inline size_t url::parse_port(std::string_view view,
5702 bool check_trailing_content) noexcept {
5703 ada_log("parse_port('", view, "') ", view.size());
5704 uint16_t parsed_port{};
5705 auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
5706 if (r.ec == std::errc::result_out_of_range) {
5707 ada_log("parse_port: std::errc::result_out_of_range");
5708 is_valid = false;
5709 return 0;
5710 }
5711 ada_log("parse_port: ", parsed_port);
5712 const size_t consumed = size_t(r.ptr - view.data());
5713 ada_log("parse_port: consumed ", consumed);
5714 if (check_trailing_content) {
5715 is_valid &=
5716 (consumed == view.size() || view[consumed] == '/' ||
5717 view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
5718 }
5719 ada_log("parse_port: is_valid = ", is_valid);
5720 if (is_valid) {
5721 // scheme_default_port can return 0, and we should allow 0 as a base port.
5722 auto default_port = scheme_default_port();
5723 bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
5724 (default_port != parsed_port);
5725 port = (r.ec == std::errc() && is_port_valid)
5726 ? std::optional<uint16_t>(parsed_port)
5727 : std::nullopt;
5728 }
5729 return consumed;
5730 }
5731
5732 } // namespace ada
5733
5734 #endif // ADA_URL_H
5735 /* end file include/ada/url-inl.h */
5736 /* begin file include/ada/url_aggregator-inl.h */
5737 /**
5738 * @file url_aggregator-inl.h
5739 * @brief Inline functions for url aggregator
5740 */
5741 #ifndef ADA_URL_AGGREGATOR_INL_H
5742 #define ADA_URL_AGGREGATOR_INL_H
5743
5744 /* begin file include/ada/unicode-inl.h */
5745 /**
5746 * @file unicode-inl.h
5747 * @brief Definitions for unicode operations.
5748 */
5749 #ifndef ADA_UNICODE_INL_H
5750 #define ADA_UNICODE_INL_H
5751 #include <algorithm>
5752
5753 /**
5754 * @namespace ada::unicode
5755 * @brief Includes the declarations for unicode operations
5756 */
5757 namespace ada::unicode {
5758 ada_really_inline size_t percent_encode_index(const std::string_view input,
5759 const uint8_t character_set[]) {
5760 return std::distance(
5761 input.begin(),
5762 std::find_if(input.begin(), input.end(), [character_set](const char c) {
5763 return character_sets::bit_at(character_set, c);
5764 }));
5765 }
5766 } // namespace ada::unicode
5767
5768 #endif // ADA_UNICODE_INL_H
5769 /* end file include/ada/unicode-inl.h */
5770
5771 #include <optional>
5772 #include <string_view>
5773
5774 namespace ada {
5775
5776 inline void url_aggregator::update_base_authority(
5777 std::string_view base_buffer, const ada::url_components &base) {
5778 std::string_view input = base_buffer.substr(
5779 base.protocol_end, base.host_start - base.protocol_end);
5780 ada_log("url_aggregator::update_base_authority ", input);
5781
5782 bool input_starts_with_dash = checkers::begins_with(input, "//");
5783 uint32_t diff = components.host_start - components.protocol_end;
5784
5785 buffer.erase(components.protocol_end,
5786 components.host_start - components.protocol_end);
5787 components.username_end = components.protocol_end;
5788
5789 if (input_starts_with_dash) {
5790 input.remove_prefix(2);
5791 diff += 2; // add "//"
5792 buffer.insert(components.protocol_end, "//");
5793 components.username_end += 2;
5794 }
5795
5796 size_t password_delimiter = input.find(':');
5797
5798 // Check if input contains both username and password by checking the
5799 // delimiter: ":" A typical input that contains authority would be "user:pass"
5800 if (password_delimiter != std::string_view::npos) {
5801 // Insert both username and password
5802 std::string_view username = input.substr(0, password_delimiter);
5803 std::string_view password = input.substr(password_delimiter + 1);
5804
5805 buffer.insert(components.protocol_end + diff, username);
5806 diff += uint32_t(username.size());
5807 buffer.insert(components.protocol_end + diff, ":");
5808 components.username_end = components.protocol_end + diff;
5809 buffer.insert(components.protocol_end + diff + 1, password);
5810 diff += uint32_t(password.size()) + 1;
5811 } else if (!input.empty()) {
5812 // Insert only username
5813 buffer.insert(components.protocol_end + diff, input);
5814 components.username_end =
5815 components.protocol_end + diff + uint32_t(input.size());
5816 diff += uint32_t(input.size());
5817 }
5818
5819 components.host_start += diff;
5820
5821 if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
5822 buffer.insert(components.host_start, "@");
5823 diff++;
5824 }
5825 components.host_end += diff;
5826 components.pathname_start += diff;
5827 if (components.search_start != url_components::omitted) {
5828 components.search_start += diff;
5829 }
5830 if (components.hash_start != url_components::omitted) {
5831 components.hash_start += diff;
5832 }
5833 }
5834
5835 inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
5836 ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
5837 input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
5838 " bytes] components.hash_start = ", components.hash_start);
5839 ADA_ASSERT_TRUE(validate());
5840 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
5841 if (components.hash_start != url_components::omitted) {
5842 buffer.resize(components.hash_start);
5843 }
5844 components.hash_start = uint32_t(buffer.size());
5845 buffer += "#";
5846 bool encoding_required = unicode::percent_encode<true>(
5847 input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
5848 // When encoding_required is false, then buffer is left unchanged, and percent
5849 // encoding was not deemed required.
5850 if (!encoding_required) {
5851 buffer.append(input);
5852 }
5853 ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
5854 buffer, "' [", buffer.size(), " bytes]");
5855 ADA_ASSERT_TRUE(validate());
5856 }
5857
5858 ada_really_inline uint32_t url_aggregator::replace_and_resize(
5859 uint32_t start, uint32_t end, std::string_view input) {
5860 uint32_t current_length = end - start;
5861 uint32_t input_size = uint32_t(input.size());
5862 uint32_t new_difference = input_size - current_length;
5863
5864 if (current_length == 0) {
5865 buffer.insert(start, input);
5866 } else if (input_size == current_length) {
5867 buffer.replace(start, input_size, input);
5868 } else if (input_size < current_length) {
5869 buffer.erase(start, current_length - input_size);
5870 buffer.replace(start, input_size, input);
5871 } else {
5872 buffer.replace(start, current_length, input.substr(0, current_length));
5873 buffer.insert(start + current_length, input.substr(current_length));
5874 }
5875
5876 return new_difference;
5877 }
5878
5879 inline void url_aggregator::update_base_hostname(const std::string_view input) {
5880 ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
5881 " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
5882 ADA_ASSERT_TRUE(validate());
5883 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
5884
5885 // This next line is required for when parsing a URL like `foo://`
5886 add_authority_slashes_if_needed();
5887
5888 bool has_credentials = components.protocol_end + 2 < components.host_start;
5889 uint32_t new_difference =
5890 replace_and_resize(components.host_start, components.host_end, input);
5891
5892 if (has_credentials) {
5893 buffer.insert(components.host_start, "@");
5894 new_difference++;
5895 }
5896 components.host_end += new_difference;
5897 components.pathname_start += new_difference;
5898 if (components.search_start != url_components::omitted) {
5899 components.search_start += new_difference;
5900 }
5901 if (components.hash_start != url_components::omitted) {
5902 components.hash_start += new_difference;
5903 }
5904 ADA_ASSERT_TRUE(validate());
5905 }
5906
5907 [[nodiscard]] ada_really_inline uint32_t
5908 url_aggregator::get_pathname_length() const noexcept {
5909 ada_log("url_aggregator::get_pathname_length");
5910 uint32_t ending_index = uint32_t(buffer.size());
5911 if (components.search_start != url_components::omitted) {
5912 ending_index = components.search_start;
5913 } else if (components.hash_start != url_components::omitted) {
5914 ending_index = components.hash_start;
5915 }
5916 return ending_index - components.pathname_start;
5917 }
5918
5919 [[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
5920 const noexcept {
5921 return buffer.size() == components.pathname_start;
5922 }
5923
5924 inline void url_aggregator::update_base_search(std::string_view input) {
5925 ada_log("url_aggregator::update_base_search ", input);
5926 ADA_ASSERT_TRUE(validate());
5927 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
5928 if (input.empty()) {
5929 clear_search();
5930 return;
5931 }
5932
5933 if (input[0] == '?') {
5934 input.remove_prefix(1);
5935 }
5936
5937 if (components.hash_start == url_components::omitted) {
5938 if (components.search_start == url_components::omitted) {
5939 components.search_start = uint32_t(buffer.size());
5940 buffer += "?";
5941 } else {
5942 buffer.resize(components.search_start + 1);
5943 }
5944
5945 buffer.append(input);
5946 } else {
5947 if (components.search_start == url_components::omitted) {
5948 components.search_start = components.hash_start;
5949 } else {
5950 buffer.erase(components.search_start,
5951 components.hash_start - components.search_start);
5952 components.hash_start = components.search_start;
5953 }
5954
5955 buffer.insert(components.search_start, "?");
5956 buffer.insert(components.search_start + 1, input);
5957 components.hash_start += uint32_t(input.size() + 1); // Do not forget `?`
5958 }
5959
5960 ADA_ASSERT_TRUE(validate());
5961 }
5962
5963 inline void url_aggregator::update_base_search(
5964 std::string_view input, const uint8_t query_percent_encode_set[]) {
5965 ada_log("url_aggregator::update_base_search ", input,
5966 " with encoding parameter ", to_string(), "\n", to_diagram());
5967 ADA_ASSERT_TRUE(validate());
5968 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
5969
5970 if (components.hash_start == url_components::omitted) {
5971 if (components.search_start == url_components::omitted) {
5972 components.search_start = uint32_t(buffer.size());
5973 buffer += "?";
5974 } else {
5975 buffer.resize(components.search_start + 1);
5976 }
5977
5978 bool encoding_required =
5979 unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
5980 // When encoding_required is false, then buffer is left unchanged, and
5981 // percent encoding was not deemed required.
5982 if (!encoding_required) {
5983 buffer.append(input);
5984 }
5985 } else {
5986 if (components.search_start == url_components::omitted) {
5987 components.search_start = components.hash_start;
5988 } else {
5989 buffer.erase(components.search_start,
5990 components.hash_start - components.search_start);
5991 components.hash_start = components.search_start;
5992 }
5993
5994 buffer.insert(components.search_start, "?");
5995 size_t idx =
5996 ada::unicode::percent_encode_index(input, query_percent_encode_set);
5997 if (idx == input.size()) {
5998 buffer.insert(components.search_start + 1, input);
5999 components.hash_start += uint32_t(input.size() + 1); // Do not forget `?`
6000 } else {
6001 buffer.insert(components.search_start + 1, input, 0, idx);
6002 input.remove_prefix(idx);
6003 // We only create a temporary string if we need percent encoding and
6004 // we attempt to create as small a temporary string as we can.
6005 std::string encoded =
6006 ada::unicode::percent_encode(input, query_percent_encode_set);
6007 buffer.insert(components.search_start + idx + 1, encoded);
6008 components.hash_start +=
6009 uint32_t(encoded.size() + idx + 1); // Do not forget `?`
6010 }
6011 }
6012
6013 ADA_ASSERT_TRUE(validate());
6014 }
6015
6016 inline void url_aggregator::update_base_pathname(const std::string_view input) {
6017 ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
6018 " bytes] \n", to_diagram());
6019 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
6020 ADA_ASSERT_TRUE(validate());
6021
6022 const bool begins_with_dashdash = checkers::begins_with(input, "//");
6023 if (!begins_with_dashdash && has_dash_dot()) {
6024 ada_log("url_aggregator::update_base_pathname has /.: \n", to_diagram());
6025 // We must delete the ./
6026 delete_dash_dot();
6027 }
6028
6029 if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
6030 !has_dash_dot()) {
6031 // If url's host is null, url does not have an opaque path, url's path's
6032 // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
6033 // output.
6034 buffer.insert(components.pathname_start, "/.");
6035 components.pathname_start += 2;
6036 }
6037
6038 uint32_t difference = replace_and_resize(
6039 components.pathname_start,
6040 components.pathname_start + get_pathname_length(), input);
6041 if (components.search_start != url_components::omitted) {
6042 components.search_start += difference;
6043 }
6044 if (components.hash_start != url_components::omitted) {
6045 components.hash_start += difference;
6046 }
6047 ada_log("url_aggregator::update_base_pathname end '", input, "' [",
6048 input.size(), " bytes] \n", to_diagram());
6049 ADA_ASSERT_TRUE(validate());
6050 }
6051
6052 inline void url_aggregator::append_base_pathname(const std::string_view input) {
6053 ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
6054 "\n", to_diagram());
6055 ADA_ASSERT_TRUE(validate());
6056 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
6057 #if ADA_DEVELOPMENT_CHECKS
6058 // computing the expected password.
6059 std::string path_expected = std::string(get_pathname());
6060 path_expected.append(input);
6061 #endif // ADA_DEVELOPMENT_CHECKS
6062 uint32_t ending_index = uint32_t(buffer.size());
6063 if (components.search_start != url_components::omitted) {
6064 ending_index = components.search_start;
6065 } else if (components.hash_start != url_components::omitted) {
6066 ending_index = components.hash_start;
6067 }
6068 buffer.insert(ending_index, input);
6069
6070 if (components.search_start != url_components::omitted) {
6071 components.search_start += uint32_t(input.size());
6072 }
6073 if (components.hash_start != url_components::omitted) {
6074 components.hash_start += uint32_t(input.size());
6075 }
6076 #if ADA_DEVELOPMENT_CHECKS
6077 std::string path_after = std::string(get_pathname());
6078 ADA_ASSERT_EQUAL(
6079 path_expected, path_after,
6080 "append_base_pathname problem after inserting " + std::string(input));
6081 #endif // ADA_DEVELOPMENT_CHECKS
6082 ADA_ASSERT_TRUE(validate());
6083 }
6084
6085 inline void url_aggregator::update_base_username(const std::string_view input) {
6086 ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
6087 "\n", to_diagram());
6088 ADA_ASSERT_TRUE(validate());
6089 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
6090
6091 add_authority_slashes_if_needed();
6092
6093 bool has_password = has_non_empty_password();
6094 bool host_starts_with_at = buffer.size() > components.host_start &&
6095 buffer[components.host_start] == '@';
6096 uint32_t diff = replace_and_resize(components.protocol_end + 2,
6097 components.username_end, input);
6098
6099 components.username_end += diff;
6100 components.host_start += diff;
6101
6102 if (!input.empty() && !host_starts_with_at) {
6103 buffer.insert(components.host_start, "@");
6104 diff++;
6105 } else if (input.empty() && host_starts_with_at && !has_password) {
6106 // Input is empty, there is no password, and we need to remove "@" from
6107 // hostname
6108 buffer.erase(components.host_start, 1);
6109 diff--;
6110 }
6111
6112 components.host_end += diff;
6113 components.pathname_start += diff;
6114 if (components.search_start != url_components::omitted) {
6115 components.search_start += diff;
6116 }
6117 if (components.hash_start != url_components::omitted) {
6118 components.hash_start += diff;
6119 }
6120 ADA_ASSERT_TRUE(validate());
6121 }
6122
6123 inline void url_aggregator::append_base_username(const std::string_view input) {
6124 ada_log("url_aggregator::append_base_username ", input);
6125 ADA_ASSERT_TRUE(validate());
6126 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
6127 #if ADA_DEVELOPMENT_CHECKS
6128 // computing the expected password.
6129 std::string username_expected = std::string(get_username());
6130 username_expected.append(input);
6131 #endif // ADA_DEVELOPMENT_CHECKS
6132 add_authority_slashes_if_needed();
6133
6134 // If input is empty, do nothing.
6135 if (input.empty()) {
6136 return;
6137 }
6138
6139 uint32_t difference = uint32_t(input.size());
6140 buffer.insert(components.username_end, input);
6141 components.username_end += difference;
6142 components.host_start += difference;
6143
6144 if (buffer[components.host_start] != '@' &&
6145 components.host_start != components.host_end) {
6146 buffer.insert(components.host_start, "@");
6147 difference++;
6148 }
6149
6150 components.host_end += difference;
6151 components.pathname_start += difference;
6152 if (components.search_start != url_components::omitted) {
6153 components.search_start += difference;
6154 }
6155 if (components.hash_start != url_components::omitted) {
6156 components.hash_start += difference;
6157 }
6158 #if ADA_DEVELOPMENT_CHECKS
6159 std::string username_after = std::string(get_username());
6160 ADA_ASSERT_EQUAL(
6161 username_expected, username_after,
6162 "append_base_username problem after inserting " + std::string(input));
6163 #endif // ADA_DEVELOPMENT_CHECKS
6164 ADA_ASSERT_TRUE(validate());
6165 }
6166
6167 inline void url_aggregator::clear_password() {
6168 ada_log("url_aggregator::clear_password ", to_string(), "\n", to_diagram());
6169 ADA_ASSERT_TRUE(validate());
6170 if (!has_password()) {
6171 return;
6172 }
6173
6174 uint32_t diff = components.host_start - components.username_end;
6175 buffer.erase(components.username_end, diff);
6176 components.host_start -= diff;
6177 components.host_end -= diff;
6178 components.pathname_start -= diff;
6179 if (components.search_start != url_components::omitted) {
6180 components.search_start -= diff;
6181 }
6182 if (components.hash_start != url_components::omitted) {
6183 components.hash_start -= diff;
6184 }
6185 }
6186
6187 inline void url_aggregator::update_base_password(const std::string_view input) {
6188 ada_log("url_aggregator::update_base_password ", input);
6189 ADA_ASSERT_TRUE(validate());
6190 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
6191
6192 add_authority_slashes_if_needed();
6193
6194 // TODO: Optimization opportunity. Merge the following removal functions.
6195 if (input.empty()) {
6196 clear_password();
6197
6198 // Remove username too, if it is empty.
6199 if (!has_non_empty_username()) {
6200 update_base_username("");
6201 }
6202
6203 return;
6204 }
6205
6206 bool password_exists = has_password();
6207 uint32_t difference = uint32_t(input.size());
6208
6209 if (password_exists) {
6210 uint32_t current_length =
6211 components.host_start - components.username_end - 1;
6212 buffer.erase(components.username_end + 1, current_length);
6213 difference -= current_length;
6214 } else {
6215 buffer.insert(components.username_end, ":");
6216 difference++;
6217 }
6218
6219 buffer.insert(components.username_end + 1, input);
6220 components.host_start += difference;
6221
6222 // The following line is required to add "@" to hostname. When updating
6223 // password if hostname does not start with "@", it is "update_base_password"s
6224 // responsibility to set it.
6225 if (buffer[components.host_start] != '@') {
6226 buffer.insert(components.host_start, "@");
6227 difference++;
6228 }
6229
6230 components.host_end += difference;
6231 components.pathname_start += difference;
6232 if (components.search_start != url_components::omitted) {
6233 components.search_start += difference;
6234 }
6235 if (components.hash_start != url_components::omitted) {
6236 components.hash_start += difference;
6237 }
6238 ADA_ASSERT_TRUE(validate());
6239 }
6240
6241 inline void url_aggregator::append_base_password(const std::string_view input) {
6242 ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
6243 "\n", to_diagram());
6244 ADA_ASSERT_TRUE(validate());
6245 ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
6246 #if ADA_DEVELOPMENT_CHECKS
6247 // computing the expected password.
6248 std::string password_expected = std::string(get_password());
6249 password_expected.append(input);
6250 #endif // ADA_DEVELOPMENT_CHECKS
6251 add_authority_slashes_if_needed();
6252
6253 // If input is empty, do nothing.
6254 if (input.empty()) {
6255 return;
6256 }
6257
6258 uint32_t difference = uint32_t(input.size());
6259 if (has_password()) {
6260 buffer.insert(components.host_start, input);
6261 } else {
6262 difference++; // Increment for ":"
6263 buffer.insert(components.username_end, ":");
6264 buffer.insert(components.username_end + 1, input);
6265 }
6266 components.host_start += difference;
6267
6268 // The following line is required to add "@" to hostname. When updating
6269 // password if hostname does not start with "@", it is "append_base_password"s
6270 // responsibility to set it.
6271 if (buffer[components.host_start] != '@') {
6272 buffer.insert(components.host_start, "@");
6273 difference++;
6274 }
6275
6276 components.host_end += difference;
6277 components.pathname_start += difference;
6278 if (components.search_start != url_components::omitted) {
6279 components.search_start += difference;
6280 }
6281 if (components.hash_start != url_components::omitted) {
6282 components.hash_start += difference;
6283 }
6284 #if ADA_DEVELOPMENT_CHECKS
6285 std::string password_after = std::string(get_password());
6286 ADA_ASSERT_EQUAL(
6287 password_expected, password_after,
6288 "append_base_password problem after inserting " + std::string(input));
6289 #endif // ADA_DEVELOPMENT_CHECKS
6290 ADA_ASSERT_TRUE(validate());
6291 }
6292
6293 inline void url_aggregator::update_base_port(uint32_t input) {
6294 ada_log("url_aggregator::update_base_port");
6295 ADA_ASSERT_TRUE(validate());
6296 if (input == url_components::omitted) {
6297 clear_port();
6298 return;
6299 }
6300 // calling std::to_string(input.value()) is unfortunate given that the port
6301 // value is probably already available as a string.
6302 std::string value = helpers::concat(":", std::to_string(input));
6303 uint32_t difference = uint32_t(value.size());
6304
6305 if (components.port != url_components::omitted) {
6306 difference -= components.pathname_start - components.host_end;
6307 buffer.erase(components.host_end,
6308 components.pathname_start - components.host_end);
6309 }
6310
6311 buffer.insert(components.host_end, value);
6312 components.pathname_start += difference;
6313 if (components.search_start != url_components::omitted) {
6314 components.search_start += difference;
6315 }
6316 if (components.hash_start != url_components::omitted) {
6317 components.hash_start += difference;
6318 }
6319 components.port = input;
6320 ADA_ASSERT_TRUE(validate());
6321 }
6322
6323 inline void url_aggregator::clear_port() {
6324 ada_log("url_aggregator::clear_port");
6325 ADA_ASSERT_TRUE(validate());
6326 if (components.port == url_components::omitted) {
6327 return;
6328 }
6329 uint32_t length = components.pathname_start - components.host_end;
6330 buffer.erase(components.host_end, length);
6331 components.pathname_start -= length;
6332 if (components.search_start != url_components::omitted) {
6333 components.search_start -= length;
6334 }
6335 if (components.hash_start != url_components::omitted) {
6336 components.hash_start -= length;
6337 }
6338 components.port = url_components::omitted;
6339 ADA_ASSERT_TRUE(validate());
6340 }
6341
6342 [[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
6343 ada_log("url_aggregator::retrieve_base_port");
6344 return components.port;
6345 }
6346
6347 inline void url_aggregator::clear_search() {
6348 ada_log("url_aggregator::clear_search");
6349 ADA_ASSERT_TRUE(validate());
6350 if (components.search_start == url_components::omitted) {
6351 return;
6352 }
6353
6354 if (components.hash_start == url_components::omitted) {
6355 buffer.resize(components.search_start);
6356 } else {
6357 buffer.erase(components.search_start,
6358 components.hash_start - components.search_start);
6359 components.hash_start = components.search_start;
6360 }
6361
6362 components.search_start = url_components::omitted;
6363
6364 #if ADA_DEVELOPMENT_CHECKS
6365 ADA_ASSERT_EQUAL(get_search(), "",
6366 "search should have been cleared on buffer=" + buffer +
6367 " with " + components.to_string() + "\n" + to_diagram());
6368 #endif
6369 ADA_ASSERT_TRUE(validate());
6370 }
6371
6372 inline void url_aggregator::clear_hash() {
6373 ada_log("url_aggregator::clear_hash");
6374 ADA_ASSERT_TRUE(validate());
6375 if (components.hash_start == url_components::omitted) {
6376 return;
6377 }
6378 buffer.resize(components.hash_start);
6379 components.hash_start = url_components::omitted;
6380
6381 #if ADA_DEVELOPMENT_CHECKS
6382 ADA_ASSERT_EQUAL(get_hash(), "",
6383 "hash should have been cleared on buffer=" + buffer +
6384 " with " + components.to_string() + "\n" + to_diagram());
6385 #endif
6386 ADA_ASSERT_TRUE(validate());
6387 }
6388
6389 inline void url_aggregator::clear_pathname() {
6390 ada_log("url_aggregator::clear_pathname");
6391 ADA_ASSERT_TRUE(validate());
6392 uint32_t ending_index = uint32_t(buffer.size());
6393 if (components.search_start != url_components::omitted) {
6394 ending_index = components.search_start;
6395 } else if (components.hash_start != url_components::omitted) {
6396 ending_index = components.hash_start;
6397 }
6398 uint32_t pathname_length = ending_index - components.pathname_start;
6399 buffer.erase(components.pathname_start, pathname_length);
6400 uint32_t difference = pathname_length;
6401 if (components.pathname_start == components.host_end + 2 &&
6402 buffer[components.host_end] == '/' &&
6403 buffer[components.host_end + 1] == '.') {
6404 components.pathname_start -= 2;
6405 buffer.erase(components.host_end, 2);
6406 difference += 2;
6407 }
6408 if (components.search_start != url_components::omitted) {
6409 components.search_start -= difference;
6410 }
6411 if (components.hash_start != url_components::omitted) {
6412 components.hash_start -= difference;
6413 }
6414 ada_log("url_aggregator::clear_pathname completed, running checks...");
6415 #if ADA_DEVELOPMENT_CHECKS
6416 ADA_ASSERT_EQUAL(get_pathname(), "",
6417 "pathname should have been cleared on buffer=" + buffer +
6418 " with " + components.to_string() + "\n" + to_diagram());
6419 #endif
6420 ADA_ASSERT_TRUE(validate());
6421 ada_log("url_aggregator::clear_pathname completed, running checks... ok");
6422 }
6423
6424 inline void url_aggregator::clear_hostname() {
6425 ada_log("url_aggregator::clear_hostname");
6426 ADA_ASSERT_TRUE(validate());
6427 if (!has_authority()) {
6428 return;
6429 }
6430 ADA_ASSERT_TRUE(has_authority());
6431
6432 uint32_t hostname_length = components.host_end - components.host_start;
6433 uint32_t start = components.host_start;
6434
6435 // If hostname starts with "@", we should not remove that character.
6436 if (hostname_length > 0 && buffer[start] == '@') {
6437 start++;
6438 hostname_length--;
6439 }
6440 buffer.erase(start, hostname_length);
6441 components.host_end = start;
6442 components.pathname_start -= hostname_length;
6443 if (components.search_start != url_components::omitted) {
6444 components.search_start -= hostname_length;
6445 }
6446 if (components.hash_start != url_components::omitted) {
6447 components.hash_start -= hostname_length;
6448 }
6449 #if ADA_DEVELOPMENT_CHECKS
6450 ADA_ASSERT_EQUAL(get_hostname(), "",
6451 "hostname should have been cleared on buffer=" + buffer +
6452 " with " + components.to_string() + "\n" + to_diagram());
6453 #endif
6454 ADA_ASSERT_TRUE(has_authority());
6455 ADA_ASSERT_EQUAL(has_empty_hostname(), true,
6456 "hostname should have been cleared on buffer=" + buffer +
6457 " with " + components.to_string() + "\n" + to_diagram());
6458 ADA_ASSERT_TRUE(validate());
6459 }
6460
6461 [[nodiscard]] inline bool url_aggregator::has_hash() const noexcept {
6462 ada_log("url_aggregator::has_hash");
6463 return components.hash_start != url_components::omitted;
6464 }
6465
6466 [[nodiscard]] inline bool url_aggregator::has_search() const noexcept {
6467 ada_log("url_aggregator::has_search");
6468 return components.search_start != url_components::omitted;
6469 }
6470
6471 ada_really_inline bool url_aggregator::has_credentials() const noexcept {
6472 ada_log("url_aggregator::has_credentials");
6473 return has_non_empty_username() || has_non_empty_password();
6474 }
6475
6476 inline bool url_aggregator::cannot_have_credentials_or_port() const {
6477 ada_log("url_aggregator::cannot_have_credentials_or_port");
6478 return type == ada::scheme::type::FILE ||
6479 components.host_start == components.host_end;
6480 }
6481
6482 [[nodiscard]] ada_really_inline const ada::url_components &
6483 url_aggregator::get_components() const noexcept {
6484 return components;
6485 }
6486
6487 [[nodiscard]] inline bool ada::url_aggregator::has_authority() const noexcept {
6488 ada_log("url_aggregator::has_authority");
6489 // Performance: instead of doing this potentially expensive check, we could
6490 // have a boolean in the struct.
6491 return components.protocol_end + 2 <= components.host_start &&
6492 helpers::substring(buffer, components.protocol_end,
6493 components.protocol_end + 2) == "//";
6494 }
6495
6496 inline void ada::url_aggregator::add_authority_slashes_if_needed() noexcept {
6497 ada_log("url_aggregator::add_authority_slashes_if_needed");
6498 ADA_ASSERT_TRUE(validate());
6499 // Protocol setter will insert `http:` to the URL. It is up to hostname setter
6500 // to insert
6501 // `//` initially to the buffer, since it depends on the hostname existence.
6502 if (has_authority()) {
6503 return;
6504 }
6505 // Performance: the common case is components.protocol_end == buffer.size()
6506 // Optimization opportunity: in many cases, the "//" is part of the input and
6507 // the insert could be fused with another insert.
6508 buffer.insert(components.protocol_end, "//");
6509 components.username_end += 2;
6510 components.host_start += 2;
6511 components.host_end += 2;
6512 components.pathname_start += 2;
6513 if (components.search_start != url_components::omitted) {
6514 components.search_start += 2;
6515 }
6516 if (components.hash_start != url_components::omitted) {
6517 components.hash_start += 2;
6518 }
6519 ADA_ASSERT_TRUE(validate());
6520 }
6521
6522 inline void ada::url_aggregator::reserve(uint32_t capacity) {
6523 buffer.reserve(capacity);
6524 }
6525
6526 inline bool url_aggregator::has_non_empty_username() const noexcept {
6527 ada_log("url_aggregator::has_non_empty_username");
6528 return components.protocol_end + 2 < components.username_end;
6529 }
6530
6531 inline bool url_aggregator::has_non_empty_password() const noexcept {
6532 ada_log("url_aggregator::has_non_empty_password");
6533 return components.host_start - components.username_end > 0;
6534 }
6535
6536 inline bool url_aggregator::has_password() const noexcept {
6537 ada_log("url_aggregator::has_password");
6538 // This function does not care about the length of the password
6539 return components.host_start > components.username_end &&
6540 buffer[components.username_end] == ':';
6541 }
6542
6543 inline bool url_aggregator::has_empty_hostname() const noexcept {
6544 if (!has_hostname()) {
6545 return false;
6546 }
6547 if (components.host_start == components.host_end) {
6548 return true;
6549 }
6550 if (components.host_end > components.host_start + 1) {
6551 return false;
6552 }
6553 return components.username_end != components.host_start;
6554 }
6555
6556 inline bool url_aggregator::has_hostname() const noexcept {
6557 return has_authority();
6558 }
6559
6560 inline bool url_aggregator::has_port() const noexcept {
6561 ada_log("url_aggregator::has_port");
6562 // A URL cannot have a username/password/port if its host is null or the empty
6563 // string, or its scheme is "file".
6564 return has_hostname() && components.pathname_start != components.host_end;
6565 }
6566
6567 [[nodiscard]] inline bool url_aggregator::has_dash_dot() const noexcept {
6568 // If url's host is null, url does not have an opaque path, url's path's size
6569 // is greater than 1, and url's path[0] is the empty string, then append
6570 // U+002F (/) followed by U+002E (.) to output.
6571 ada_log("url_aggregator::has_dash_dot");
6572 #if ADA_DEVELOPMENT_CHECKS
6573 // If pathname_start and host_end are exactly two characters apart, then we
6574 // either have a one-digit port such as http://test.com:5?param=1 or else we
6575 // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
6576 if (components.pathname_start == components.host_end + 2) {
6577 ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
6578 buffer[components.host_end + 1] == '.') ||
6579 (buffer[components.host_end] == ':' &&
6580 checkers::is_digit(buffer[components.host_end + 1])));
6581 }
6582 if (components.pathname_start == components.host_end + 2 &&
6583 buffer[components.host_end] == '/' &&
6584 buffer[components.host_end + 1] == '.') {
6585 ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
6586 ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
6587 ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
6588 }
6589 #endif
6590 // Performance: it should be uncommon for components.pathname_start ==
6591 // components.host_end + 2 to be true. So we put this check first in the
6592 // sequence. Most times, we do not have an opaque path. Checking for '/.' is
6593 // more expensive, but should be uncommon.
6594 return components.pathname_start == components.host_end + 2 &&
6595 !has_opaque_path && buffer[components.host_end] == '/' &&
6596 buffer[components.host_end + 1] == '.';
6597 }
6598
6599 [[nodiscard]] inline std::string_view url_aggregator::get_href()
6600 const noexcept {
6601 ada_log("url_aggregator::get_href");
6602 return buffer;
6603 }
6604
6605 ada_really_inline size_t url_aggregator::parse_port(
6606 std::string_view view, bool check_trailing_content) noexcept {
6607 ada_log("url_aggregator::parse_port('", view, "') ", view.size());
6608 uint16_t parsed_port{};
6609 auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
6610 if (r.ec == std::errc::result_out_of_range) {
6611 ada_log("parse_port: std::errc::result_out_of_range");
6612 is_valid = false;
6613 return 0;
6614 }
6615 ada_log("parse_port: ", parsed_port);
6616 const size_t consumed = size_t(r.ptr - view.data());
6617 ada_log("parse_port: consumed ", consumed);
6618 if (check_trailing_content) {
6619 is_valid &=
6620 (consumed == view.size() || view[consumed] == '/' ||
6621 view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
6622 }
6623 ada_log("parse_port: is_valid = ", is_valid);
6624 if (is_valid) {
6625 ada_log("parse_port", r.ec == std::errc());
6626 // scheme_default_port can return 0, and we should allow 0 as a base port.
6627 auto default_port = scheme_default_port();
6628 bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
6629 (default_port != parsed_port);
6630 if (r.ec == std::errc() && is_port_valid) {
6631 update_base_port(parsed_port);
6632 } else {
6633 clear_port();
6634 }
6635 }
6636 return consumed;
6637 }
6638
6639 inline void url_aggregator::set_protocol_as_file() {
6640 ada_log("url_aggregator::set_protocol_as_file ");
6641 ADA_ASSERT_TRUE(validate());
6642 type = ada::scheme::type::FILE;
6643 // next line could overflow but unsigned arithmetic has well-defined
6644 // overflows.
6645 uint32_t new_difference = 5 - components.protocol_end;
6646
6647 if (buffer.empty()) {
6648 buffer.append("file:");
6649 } else {
6650 buffer.erase(0, components.protocol_end);
6651 buffer.insert(0, "file:");
6652 }
6653 components.protocol_end = 5;
6654
6655 // Update the rest of the components.
6656 components.username_end += new_difference;
6657 components.host_start += new_difference;
6658 components.host_end += new_difference;
6659 components.pathname_start += new_difference;
6660 if (components.search_start != url_components::omitted) {
6661 components.search_start += new_difference;
6662 }
6663 if (components.hash_start != url_components::omitted) {
6664 components.hash_start += new_difference;
6665 }
6666 ADA_ASSERT_TRUE(validate());
6667 }
6668
6669 inline std::ostream &operator<<(std::ostream &out,
6670 const ada::url_aggregator &u) {
6671 return out << u.to_string();
6672 }
6673 } // namespace ada
6674
6675 #endif // ADA_URL_AGGREGATOR_INL_H
6676 /* end file include/ada/url_aggregator-inl.h */
6677 /* begin file include/ada/url_search_params.h */
6678 /**
6679 * @file url_search_params.h
6680 * @brief Declaration for the URL Search Params
6681 */
6682 #ifndef ADA_URL_SEARCH_PARAMS_H
6683 #define ADA_URL_SEARCH_PARAMS_H
6684
6685 #include <optional>
6686 #include <string>
6687 #include <string_view>
6688 #include <vector>
6689
6690 namespace ada {
6691
6692 enum class url_search_params_iter_type {
6693 KEYS,
6694 VALUES,
6695 ENTRIES,
6696 };
6697
6698 template <typename T, url_search_params_iter_type Type>
6699 struct url_search_params_iter;
6700
6701 typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
6702
6703 using url_search_params_keys_iter =
6704 url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
6705 using url_search_params_values_iter =
6706 url_search_params_iter<std::string_view,
6707 url_search_params_iter_type::VALUES>;
6708 using url_search_params_entries_iter =
6709 url_search_params_iter<key_value_view_pair,
6710 url_search_params_iter_type::ENTRIES>;
6711
6712 /**
6713 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
6714 */
6715 struct url_search_params {
6716 url_search_params() = default;
6717
6718 /**
6719 * @see
6720 * https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-constructor.any.js
6721 */
6722 url_search_params(const std::string_view input) { initialize(input); }
6723
6724 url_search_params(const url_search_params &u) = default;
6725 url_search_params(url_search_params &&u) noexcept = default;
6726 url_search_params &operator=(url_search_params &&u) noexcept = default;
6727 url_search_params &operator=(const url_search_params &u) = default;
6728 ~url_search_params() = default;
6729
6730 [[nodiscard]] inline size_t size() const noexcept;
6731
6732 /**
6733 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
6734 */
6735 inline void append(std::string_view key, std::string_view value);
6736
6737 /**
6738 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
6739 */
6740 inline void remove(std::string_view key);
6741 inline void remove(std::string_view key, std::string_view value);
6742
6743 /**
6744 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
6745 */
6746 inline std::optional<std::string_view> get(std::string_view key);
6747
6748 /**
6749 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
6750 */
6751 inline std::vector<std::string> get_all(std::string_view key);
6752
6753 /**
6754 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
6755 */
6756 inline bool has(std::string_view key) noexcept;
6757 inline bool has(std::string_view key, std::string_view value) noexcept;
6758
6759 /**
6760 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
6761 */
6762 inline void set(std::string_view key, std::string_view value);
6763
6764 /**
6765 * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
6766 */
6767 inline void sort();
6768
6769 /**
6770 * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
6771 */
6772 inline std::string to_string();
6773
6774 /**
6775 * Returns a simple JS-style iterator over all of the keys in this
6776 * url_search_params. The keys in the iterator are not unique. The valid
6777 * lifespan of the iterator is tied to the url_search_params. The iterator
6778 * must be freed when you're done with it.
6779 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
6780 */
6781 inline url_search_params_keys_iter get_keys();
6782
6783 /**
6784 * Returns a simple JS-style iterator over all of the values in this
6785 * url_search_params. The valid lifespan of the iterator is tied to the
6786 * url_search_params. The iterator must be freed when you're done with it.
6787 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
6788 */
6789 inline url_search_params_values_iter get_values();
6790
6791 /**
6792 * Returns a simple JS-style iterator over all of the entries in this
6793 * url_search_params. The entries are pairs of keys and corresponding values.
6794 * The valid lifespan of the iterator is tied to the url_search_params. The
6795 * iterator must be freed when you're done with it.
6796 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
6797 */
6798 inline url_search_params_entries_iter get_entries();
6799
6800 /**
6801 * C++ style conventional iterator support. const only because we
6802 * do not really want the params to be modified via the iterator.
6803 */
6804 inline auto begin() const { return params.begin(); }
6805 inline auto end() const { return params.end(); }
6806 inline auto front() const { return params.front(); }
6807 inline auto back() const { return params.back(); }
6808 inline auto operator[](size_t index) const { return params[index]; }
6809
6810 private:
6811 typedef std::pair<std::string, std::string> key_value_pair;
6812 std::vector<key_value_pair> params{};
6813
6814 /**
6815 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
6816 */
6817 void initialize(std::string_view init);
6818
6819 template <typename T, url_search_params_iter_type Type>
6820 friend struct url_search_params_iter;
6821 }; // url_search_params
6822
6823 /**
6824 * Implements a non-conventional iterator pattern that is closer in style to
6825 * JavaScript's definition of an iterator.
6826 *
6827 * @see https://webidl.spec.whatwg.org/#idl-iterable
6828 */
6829 template <typename T, url_search_params_iter_type Type>
6830 struct url_search_params_iter {
6831 inline url_search_params_iter() : params(EMPTY) {}
6832 url_search_params_iter(const url_search_params_iter &u) = default;
6833 url_search_params_iter(url_search_params_iter &&u) noexcept = default;
6834 url_search_params_iter &operator=(url_search_params_iter &&u) noexcept =
6835 default;
6836 url_search_params_iter &operator=(const url_search_params_iter &u) = default;
6837 ~url_search_params_iter() = default;
6838
6839 /**
6840 * Return the next item in the iterator or std::nullopt if done.
6841 */
6842 inline std::optional<T> next();
6843
6844 inline bool has_next();
6845
6846 private:
6847 static url_search_params EMPTY;
6848 inline url_search_params_iter(url_search_params ¶ms_) : params(params_) {}
6849
6850 url_search_params ¶ms;
6851 size_t pos = 0;
6852
6853 friend struct url_search_params;
6854 };
6855
6856 } // namespace ada
6857 #endif
6858 /* end file include/ada/url_search_params.h */
6859 /* begin file include/ada/url_search_params-inl.h */
6860 /**
6861 * @file url_search_params-inl.h
6862 * @brief Inline declarations for the URL Search Params
6863 */
6864 #ifndef ADA_URL_SEARCH_PARAMS_INL_H
6865 #define ADA_URL_SEARCH_PARAMS_INL_H
6866
6867
6868 #include <algorithm>
6869 #include <optional>
6870 #include <string>
6871 #include <string_view>
6872 #include <vector>
6873
6874 namespace ada {
6875
6876 // A default, empty url_search_params for use with empty iterators.
6877 template <typename T, ada::url_search_params_iter_type Type>
6878 url_search_params url_search_params_iter<T, Type>::EMPTY;
6879
6880 inline void url_search_params::initialize(std::string_view input) {
6881 if (!input.empty() && input.front() == '?') {
6882 input.remove_prefix(1);
6883 }
6884
6885 auto process_key_value = [&](const std::string_view current) {
6886 auto equal = current.find('=');
6887
6888 if (equal == std::string_view::npos) {
6889 auto name = std::string(current);
6890 std::replace(name.begin(), name.end(), '+', ' ');
6891 params.emplace_back(unicode::percent_decode(name, name.find('%')), "");
6892 } else {
6893 auto name = std::string(current.substr(0, equal));
6894 auto value = std::string(current.substr(equal + 1));
6895
6896 std::replace(name.begin(), name.end(), '+', ' ');
6897 std::replace(value.begin(), value.end(), '+', ' ');
6898
6899 params.emplace_back(unicode::percent_decode(name, name.find('%')),
6900 unicode::percent_decode(value, value.find('%')));
6901 }
6902 };
6903
6904 while (!input.empty()) {
6905 auto ampersand_index = input.find('&');
6906
6907 if (ampersand_index == std::string_view::npos) {
6908 if (!input.empty()) {
6909 process_key_value(input);
6910 }
6911 break;
6912 } else if (ampersand_index != 0) {
6913 process_key_value(input.substr(0, ampersand_index));
6914 }
6915
6916 input.remove_prefix(ampersand_index + 1);
6917 }
6918 }
6919
6920 inline void url_search_params::append(const std::string_view key,
6921 const std::string_view value) {
6922 params.emplace_back(key, value);
6923 }
6924
6925 inline size_t url_search_params::size() const noexcept { return params.size(); }
6926
6927 inline std::optional<std::string_view> url_search_params::get(
6928 const std::string_view key) {
6929 auto entry = std::find_if(params.begin(), params.end(),
6930 [&key](auto ¶m) { return param.first == key; });
6931
6932 if (entry == params.end()) {
6933 return std::nullopt;
6934 }
6935
6936 return entry->second;
6937 }
6938
6939 inline std::vector<std::string> url_search_params::get_all(
6940 const std::string_view key) {
6941 std::vector<std::string> out{};
6942
6943 for (auto ¶m : params) {
6944 if (param.first == key) {
6945 out.emplace_back(param.second);
6946 }
6947 }
6948
6949 return out;
6950 }
6951
6952 inline bool url_search_params::has(const std::string_view key) noexcept {
6953 auto entry = std::find_if(params.begin(), params.end(),
6954 [&key](auto ¶m) { return param.first == key; });
6955 return entry != params.end();
6956 }
6957
6958 inline bool url_search_params::has(std::string_view key,
6959 std::string_view value) noexcept {
6960 auto entry =
6961 std::find_if(params.begin(), params.end(), [&key, &value](auto ¶m) {
6962 return param.first == key && param.second == value;
6963 });
6964 return entry != params.end();
6965 }
6966
6967 inline std::string url_search_params::to_string() {
6968 auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
6969 std::string out{};
6970 for (size_t i = 0; i < params.size(); i++) {
6971 auto key = ada::unicode::percent_encode(params[i].first, character_set);
6972 auto value = ada::unicode::percent_encode(params[i].second, character_set);
6973
6974 // Performance optimization: Move this inside percent_encode.
6975 std::replace(key.begin(), key.end(), ' ', '+');
6976 std::replace(value.begin(), value.end(), ' ', '+');
6977
6978 if (i != 0) {
6979 out += "&";
6980 }
6981 out.append(key);
6982 out += "=";
6983 out.append(value);
6984 }
6985 return out;
6986 }
6987
6988 inline void url_search_params::set(const std::string_view key,
6989 const std::string_view value) {
6990 const auto find = [&key](auto ¶m) { return param.first == key; };
6991
6992 auto it = std::find_if(params.begin(), params.end(), find);
6993
6994 if (it == params.end()) {
6995 params.emplace_back(key, value);
6996 } else {
6997 it->second = value;
6998 params.erase(std::remove_if(std::next(it), params.end(), find),
6999 params.end());
7000 }
7001 }
7002
7003 inline void url_search_params::remove(const std::string_view key) {
7004 params.erase(
7005 std::remove_if(params.begin(), params.end(),
7006 [&key](auto ¶m) { return param.first == key; }),
7007 params.end());
7008 }
7009
7010 inline void url_search_params::remove(const std::string_view key,
7011 const std::string_view value) {
7012 params.erase(std::remove_if(params.begin(), params.end(),
7013 [&key, &value](auto ¶m) {
7014 return param.first == key &&
7015 param.second == value;
7016 }),
7017 params.end());
7018 }
7019
7020 inline void url_search_params::sort() {
7021 std::stable_sort(params.begin(), params.end(),
7022 [](const key_value_pair &lhs, const key_value_pair &rhs) {
7023 return lhs.first < rhs.first;
7024 });
7025 }
7026
7027 inline url_search_params_keys_iter url_search_params::get_keys() {
7028 return url_search_params_keys_iter(*this);
7029 }
7030
7031 /**
7032 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
7033 */
7034 inline url_search_params_values_iter url_search_params::get_values() {
7035 return url_search_params_values_iter(*this);
7036 }
7037
7038 /**
7039 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
7040 */
7041 inline url_search_params_entries_iter url_search_params::get_entries() {
7042 return url_search_params_entries_iter(*this);
7043 }
7044
7045 template <typename T, url_search_params_iter_type Type>
7046 inline bool url_search_params_iter<T, Type>::has_next() {
7047 return pos < params.params.size();
7048 }
7049
7050 template <>
7051 inline std::optional<std::string_view> url_search_params_keys_iter::next() {
7052 if (!has_next()) return std::nullopt;
7053 return params.params[pos++].first;
7054 }
7055
7056 template <>
7057 inline std::optional<std::string_view> url_search_params_values_iter::next() {
7058 if (!has_next()) return std::nullopt;
7059 return params.params[pos++].second;
7060 }
7061
7062 template <>
7063 inline std::optional<key_value_view_pair>
7064 url_search_params_entries_iter::next() {
7065 if (!has_next()) return std::nullopt;
7066 return params.params[pos++];
7067 }
7068
7069 } // namespace ada
7070
7071 #endif // ADA_URL_SEARCH_PARAMS_INL_H
7072 /* end file include/ada/url_search_params-inl.h */
7073
7074 // Public API
7075 /* begin file include/ada/ada_version.h */
7076 /**
7077 * @file ada_version.h
7078 * @brief Definitions for Ada's version number.
7079 */
7080 #ifndef ADA_ADA_VERSION_H
7081 #define ADA_ADA_VERSION_H
7082
7083 #define ADA_VERSION "2.7.6"
7084
7085 namespace ada {
7086
7087 enum {
7088 ADA_VERSION_MAJOR = 2,
7089 ADA_VERSION_MINOR = 7,
7090 ADA_VERSION_REVISION = 6,
7091 };
7092
7093 } // namespace ada
7094
7095 #endif // ADA_ADA_VERSION_H
7096 /* end file include/ada/ada_version.h */
7097 /* begin file include/ada/implementation.h */
7098 /**
7099 * @file implementation.h
7100 * @brief Definitions for user facing functions for parsing URL and it's
7101 * components.
7102 */
7103 #ifndef ADA_IMPLEMENTATION_H
7104 #define ADA_IMPLEMENTATION_H
7105
7106 #include <string>
7107 #include <optional>
7108
7109
7110 namespace ada {
7111 enum class errors { generic_error };
7112
7113 template <class result_type = ada::url_aggregator>
7114 using result = tl::expected<result_type, ada::errors>;
7115
7116 /**
7117 * The URL parser takes a scalar value string input, with an optional null or
7118 * base URL base (default null). The parser assumes the input is a valid ASCII
7119 * or UTF-8 string.
7120 *
7121 * @param input the string input to analyze (must be valid ASCII or UTF-8)
7122 * @param base_url the optional URL input to use as a base url.
7123 * @return a parsed URL.
7124 */
7125 template <class result_type = ada::url_aggregator>
7126 ada_warn_unused ada::result<result_type> parse(
7127 std::string_view input, const result_type* base_url = nullptr);
7128
7129 extern template ada::result<url> parse<url>(std::string_view input,
7130 const url* base_url);
7131 extern template ada::result<url_aggregator> parse<url_aggregator>(
7132 std::string_view input, const url_aggregator* base_url);
7133
7134 /**
7135 * Verifies whether the URL strings can be parsed. The function assumes
7136 * that the inputs are valid ASCII or UTF-8 strings.
7137 * @see https://url.spec.whatwg.org/#dom-url-canparse
7138 * @return If URL can be parsed or not.
7139 */
7140 bool can_parse(std::string_view input,
7141 const std::string_view* base_input = nullptr);
7142
7143 /**
7144 * Computes a href string from a file path. The function assumes
7145 * that the input is a valid ASCII or UTF-8 string.
7146 * @return a href string (starts with file:://)
7147 */
7148 std::string href_from_file(std::string_view path);
7149 } // namespace ada
7150
7151 #endif // ADA_IMPLEMENTATION_H
7152 /* end file include/ada/implementation.h */
7153
7154 #endif // ADA_H
7155 /* end file include/ada.h */
7156