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