Coverage Report

Created: 2026-07-14 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ada-url/build/singleheader/ada.h
Line
Count
Source
1
/* auto-generated on 2026-07-13 09:15:14 -0400. Do not edit! */
2
/* begin file include/ada.h */
3
/**
4
 * @file ada.h
5
 * @brief Main header for the Ada URL parser library.
6
 *
7
 * This is the primary entry point for the Ada URL parser library. Including
8
 * this single header provides access to the complete Ada API, including:
9
 *
10
 * - URL parsing via `ada::parse()` function
11
 * - Two URL representations: `ada::url` and `ada::url_aggregator`
12
 * - URL search parameters via `ada::url_search_params`
13
 * - URL pattern matching via `ada::url_pattern` (URLPattern API)
14
 * - IDNA (Internationalized Domain Names) support
15
 *
16
 * @example
17
 * ```cpp
18
 *
19
 * // Parse a URL
20
 * auto url = ada::parse("https://example.com/path?query=1");
21
 * if (url) {
22
 *     std::cout << url->get_hostname(); // "example.com"
23
 * }
24
 * ```
25
 *
26
 * @see https://url.spec.whatwg.org/ - WHATWG URL Standard
27
 * @see https://github.com/ada-url/ada - Ada URL Parser GitHub Repository
28
 */
29
#ifndef ADA_H
30
#define ADA_H
31
32
/* begin file include/ada/ada_idna.h */
33
/* auto-generated on 2026-07-12 20:34:08 -0400. Do not edit! */
34
/* begin file include/idna.h */
35
#ifndef ADA_IDNA_H
36
#define ADA_IDNA_H
37
38
/* begin file include/ada/idna/unicode_transcoding.h */
39
#ifndef ADA_IDNA_UNICODE_TRANSCODING_H
40
#define ADA_IDNA_UNICODE_TRANSCODING_H
41
42
#include <string>
43
#include <string_view>
44
45
namespace ada::idna {
46
47
size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output);
48
49
size_t utf8_length_from_utf32(const char32_t* buf, size_t len);
50
51
size_t utf32_length_from_utf8(const char* buf, size_t len);
52
53
size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output);
54
55
}  // namespace ada::idna
56
57
#endif  // ADA_IDNA_UNICODE_TRANSCODING_H
58
/* end file include/ada/idna/unicode_transcoding.h */
59
/* begin file include/ada/idna/mapping.h */
60
#ifndef ADA_IDNA_MAPPING_H
61
#define ADA_IDNA_MAPPING_H
62
63
#include <string>
64
#include <string_view>
65
66
namespace ada::idna {
67
68
// If the input is ascii, then the mapping is just -> lower case.
69
void ascii_map(char* input, size_t length);
70
// Map the characters according to IDNA, returning the empty string on error.
71
std::u32string map(std::u32string_view input);
72
// Map into an existing buffer (cleared on entry). Returns false if any code
73
// point is disallowed. Reusing the buffer avoids repeated heap allocations
74
// when called in a loop over multiple labels.
75
bool map(std::u32string_view input, std::u32string& out);
76
77
}  // namespace ada::idna
78
79
#endif
80
/* end file include/ada/idna/mapping.h */
81
/* begin file include/ada/idna/normalization.h */
82
#ifndef ADA_IDNA_NORMALIZATION_H
83
#define ADA_IDNA_NORMALIZATION_H
84
85
#include <string>
86
#include <string_view>
87
88
namespace ada::idna {
89
90
// Returns true if `input` is already in Unicode Normalization Form C.
91
// Requires that internal tables have been loaded (call ensure via normalize
92
// or map first, or this returns false if tables are unavailable).
93
[[nodiscard]] bool is_already_nfc(std::u32string_view input) noexcept;
94
95
// Normalize the characters according to IDNA (Unicode Normalization Form C).
96
// Returns false if the internal Unicode tables could not be loaded; in that
97
// case `input` is left unchanged. Skips work when the string is already NFC.
98
[[nodiscard]] bool normalize(std::u32string& input);
99
100
}  // namespace ada::idna
101
#endif
102
/* end file include/ada/idna/normalization.h */
103
/* begin file include/ada/idna/punycode.h */
104
#ifndef ADA_IDNA_PUNYCODE_H
105
#define ADA_IDNA_PUNYCODE_H
106
107
#include <string>
108
#include <string_view>
109
110
namespace ada::idna {
111
112
bool punycode_to_utf32(std::string_view input, std::u32string& out);
113
bool verify_punycode(std::string_view input);
114
bool utf32_to_punycode(std::u32string_view input, std::string& out);
115
116
}  // namespace ada::idna
117
118
#endif  // ADA_IDNA_PUNYCODE_H
119
/* end file include/ada/idna/punycode.h */
120
/* begin file include/ada/idna/validity.h */
121
#ifndef ADA_IDNA_VALIDITY_H
122
#define ADA_IDNA_VALIDITY_H
123
124
#include <string>
125
#include <string_view>
126
127
namespace ada::idna {
128
129
/**
130
 * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
131
 */
132
bool is_label_valid(std::u32string_view label);
133
134
}  // namespace ada::idna
135
136
#endif  // ADA_IDNA_VALIDITY_H
137
/* end file include/ada/idna/validity.h */
138
/* begin file include/ada/idna/to_ascii.h */
139
#ifndef ADA_IDNA_TO_ASCII_H
140
#define ADA_IDNA_TO_ASCII_H
141
142
#include <string>
143
#include <string_view>
144
145
/* begin file include/ada/idna/limits.h */
146
#ifndef ADA_IDNA_LIMITS_H
147
#define ADA_IDNA_LIMITS_H
148
149
#include <cstddef>
150
151
namespace ada::idna {
152
153
// Maximum accepted UTF-8 domain length for to_ascii / to_unicode.
154
// Bounds heap growth under untrusted input (DoS resistance). DNS wire limits
155
// are smaller; this allows long Unicode labels used in URL tests/fixtures.
156
inline constexpr size_t max_domain_input_bytes = 16384;
157
158
}  // namespace ada::idna
159
160
#endif  // ADA_IDNA_LIMITS_H
161
/* end file include/ada/idna/limits.h */
162
163
namespace ada::idna {
164
165
// Converts a domain (e.g., www.google.com) possibly containing international
166
// characters to an ascii domain (with punycode). It will not do percent
167
// decoding: percent decoding should be done prior to calling this function. We
168
// do not remove tabs and spaces, they should have been removed prior to calling
169
// this function. We also do not trim control characters. We also assume that
170
// the input is not empty. We return "" on error. Inputs longer than
171
// max_domain_input_bytes are rejected.
172
//
173
// This function may accept or even produce invalid domains (WHATWG carve-outs).
174
std::string to_ascii(std::string_view ut8_string);
175
176
// Same as to_ascii, but writes into `out` and returns false on error without
177
// relying on empty-string ambiguity.
178
[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out);
179
180
// Returns true if the string contains a forbidden code point according to the
181
// WHATGL URL specification:
182
// https://url.spec.whatwg.org/#forbidden-domain-code-point
183
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
184
185
bool constexpr is_ascii(std::u32string_view view);
186
bool constexpr is_ascii(std::string_view view);
187
188
}  // namespace ada::idna
189
190
#endif  // ADA_IDNA_TO_ASCII_H
191
/* end file include/ada/idna/to_ascii.h */
192
/* begin file include/ada/idna/to_unicode.h */
193
#ifndef ADA_IDNA_TO_UNICODE_H
194
#define ADA_IDNA_TO_UNICODE_H
195
196
#include <string>
197
#include <string_view>
198
199
namespace ada::idna {
200
201
// UTS #46 ToUnicode. Never fails per the standard: on step failure the original
202
// label is kept. Inputs longer than max_domain_input_bytes are returned
203
// unchanged as a safety measure under untrusted input.
204
std::string to_unicode(std::string_view input);
205
206
// Writes into `out`. Returns false only if the input exceeds
207
// max_domain_input_bytes (out is left empty). Otherwise always returns true
208
// (ToUnicode does not fail).
209
[[nodiscard]] bool to_unicode(std::string_view input, std::string& out);
210
211
}  // namespace ada::idna
212
213
#endif  // ADA_IDNA_TO_UNICODE_H
214
/* end file include/ada/idna/to_unicode.h */
215
/* begin file include/ada/idna/identifier.h */
216
#ifndef ADA_IDNA_IDENTIFIER_H
217
#define ADA_IDNA_IDENTIFIER_H
218
219
#include <string>
220
#include <string_view>
221
222
namespace ada::idna {
223
224
// Verify if it is valid name code point given a Unicode code point and a
225
// boolean first: If first is true return the result of checking if code point
226
// is contained in the IdentifierStart set of code points. Otherwise return the
227
// result of checking if code point is contained in the IdentifierPart set of
228
// code points. Returns false if the input is empty or the code point is not
229
// valid. There is minimal Unicode error handling: the input should be valid
230
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
231
bool valid_name_code_point(char32_t code_point, bool first);
232
233
}  // namespace ada::idna
234
235
#endif
236
/* end file include/ada/idna/identifier.h */
237
238
#endif
239
/* end file include/idna.h */
240
/* end file include/ada/ada_idna.h */
241
/* begin file include/ada/character_sets.h */
242
/**
243
 * @file character_sets.h
244
 * @brief Declaration of the character sets used by unicode functions.
245
 * @author Node.js
246
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
247
 */
248
#ifndef ADA_CHARACTER_SETS_H
249
#define ADA_CHARACTER_SETS_H
250
251
/* begin file include/ada/common_defs.h */
252
/**
253
 * @file common_defs.h
254
 * @brief Cross-platform compiler macros and common definitions.
255
 *
256
 * This header provides compiler-specific macros for optimization hints,
257
 * platform detection, SIMD support detection, and development/debug utilities.
258
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
259
 */
260
#ifndef ADA_COMMON_DEFS_H
261
#define ADA_COMMON_DEFS_H
262
263
// https://en.cppreference.com/w/cpp/feature_test#Library_features
264
// detect C++20 features
265
#include <version>
266
267
#ifdef _MSC_VER
268
#define ADA_VISUAL_STUDIO 1
269
/**
270
 * We want to differentiate carefully between
271
 * clang under visual studio and regular visual
272
 * studio.
273
 */
274
#ifdef __clang__
275
// clang under visual studio
276
#define ADA_CLANG_VISUAL_STUDIO 1
277
#else
278
// just regular visual studio (best guess)
279
#define ADA_REGULAR_VISUAL_STUDIO 1
280
#endif  // __clang__
281
#endif  // _MSC_VER
282
283
#if defined(__GNUC__)
284
// Marks a block with a name so that MCA analysis can see it.
285
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
286
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
287
#define ADA_DEBUG_BLOCK(name, block) \
288
  BEGIN_DEBUG_BLOCK(name);           \
289
  block;                             \
290
  END_DEBUG_BLOCK(name);
291
#else
292
#define ADA_BEGIN_DEBUG_BLOCK(name)
293
#define ADA_END_DEBUG_BLOCK(name)
294
#define ADA_DEBUG_BLOCK(name, block)
295
#endif
296
297
// Align to N-byte boundary
298
#define ADA_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
299
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1))
300
301
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0)
302
303
#if defined(ADA_REGULAR_VISUAL_STUDIO)
304
305
#define ada_really_inline __forceinline
306
#define ada_never_inline __declspec(noinline)
307
308
#define ada_unused
309
#define ada_warn_unused
310
311
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
312
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
313
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
314
  __pragma(warning(disable : WARNING_NUMBER))
315
// Get rid of Intellisense-only warnings (Code Analysis)
316
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
317
// better (_MSC_VER>=1910).
318
#ifdef __has_include
319
#if __has_include(<CppCoreCheck\Warnings.h>)
320
#include <CppCoreCheck\Warnings.h>
321
#define ADA_DISABLE_UNDESIRED_WARNINGS \
322
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
323
#endif
324
#endif
325
326
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
327
#define ADA_DISABLE_UNDESIRED_WARNINGS
328
#endif
329
330
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
331
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
332
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
333
334
#else  // ADA_REGULAR_VISUAL_STUDIO
335
336
#define ada_really_inline inline __attribute__((always_inline))
337
#define ada_never_inline inline __attribute__((noinline))
338
339
#define ada_unused __attribute__((unused))
340
#define ada_warn_unused __attribute__((warn_unused_result))
341
342
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
343
// gcc doesn't seem to disable all warnings with all and extra, add warnings
344
// here as necessary
345
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
346
  ADA_PUSH_DISABLE_WARNINGS                         \
347
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
348
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
349
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
350
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
351
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
352
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
353
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
354
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
355
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
356
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
357
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
358
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
359
#define ADA_PRAGMA(P) _Pragma(#P)
360
#define ADA_DISABLE_GCC_WARNING(WARNING) \
361
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
362
#if defined(ADA_CLANG_VISUAL_STUDIO)
363
#define ADA_DISABLE_UNDESIRED_WARNINGS \
364
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
365
#else
366
#define ADA_DISABLE_UNDESIRED_WARNINGS
367
#endif
368
#define ADA_DISABLE_DEPRECATED_WARNING \
369
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
370
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
371
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
372
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
373
374
#endif  // MSC_VER
375
376
#if defined(ADA_VISUAL_STUDIO)
377
/**
378
 * It does not matter here whether you are using
379
 * the regular visual studio or clang under visual
380
 * studio.
381
 */
382
#if ADA_USING_LIBRARY
383
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
384
#else
385
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
386
#endif
387
#else
388
#define ADA_DLLIMPORTEXPORT
389
#endif
390
391
/// If EXPR is an error, returns it.
392
#define ADA_TRY(EXPR)   \
393
  {                     \
394
    auto _err = (EXPR); \
395
    if (_err) {         \
396
      return _err;      \
397
    }                   \
398
  }
399
400
// __has_cpp_attribute is part of C++20
401
#if !defined(__has_cpp_attribute)
402
#define __has_cpp_attribute(x) 0
403
#endif
404
405
#if __has_cpp_attribute(gnu::noinline)
406
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
407
#else
408
#define ADA_ATTRIBUTE_NOINLINE
409
#endif
410
411
namespace ada {
412
0
[[noreturn]] inline void unreachable() {
413
0
#ifdef __GNUC__
414
0
  __builtin_unreachable();
415
#elif defined(_MSC_VER)
416
  __assume(false);
417
#else
418
#endif
419
0
}
420
}  // namespace ada
421
422
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
423
// we want to set it under debug builds. We detect a debug build
424
// under Visual Studio when the _DEBUG macro is set. Under the other
425
// compilers, we use the fact that they define __OPTIMIZE__ whenever
426
// they allow optimizations.
427
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
428
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
429
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
430
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
431
// to set the __OPTIMIZE__ macro).
432
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
433
#ifdef _MSC_VER
434
// Visual Studio seems to set _DEBUG for debug builds.
435
#ifdef _DEBUG
436
#define ADA_DEVELOPMENT_CHECKS 1
437
#endif  // _DEBUG
438
#else   // _MSC_VER
439
// All other compilers appear to set __OPTIMIZE__ to a positive integer
440
// when the compiler is optimizing.
441
#ifndef __OPTIMIZE__
442
#define ADA_DEVELOPMENT_CHECKS 1
443
#endif  // __OPTIMIZE__
444
#endif  // _MSC_VER
445
#endif  // ADA_DEVELOPMENT_CHECKS
446
447
#define ADA_STR(x) #x
448
449
#if ADA_DEVELOPMENT_CHECKS
450
#define ADA_REQUIRE(EXPR) \
451
  {                       \
452
    if (!(EXPR) { abort(); }) }
453
454
#define ADA_FAIL(MESSAGE)                            \
455
  do {                                               \
456
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
457
    abort();                                         \
458
  } while (0);
459
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
460
  do {                                                                         \
461
    if (LHS != RHS) {                                                          \
462
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
463
      ADA_FAIL(MESSAGE);                                                       \
464
    }                                                                          \
465
  } while (0);
466
#define ADA_ASSERT_TRUE(COND)                                               \
467
  do {                                                                      \
468
    if (!(COND)) {                                                          \
469
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
470
                << std::endl;                                               \
471
      ADA_FAIL(ADA_STR(COND));                                              \
472
    }                                                                       \
473
  } while (0);
474
#else
475
#define ADA_FAIL(MESSAGE)
476
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
477
#define ADA_ASSERT_TRUE(COND)
478
#endif
479
480
#ifdef ADA_VISUAL_STUDIO
481
#define ADA_ASSUME(COND) __assume(COND)
482
#else
483
#define ADA_ASSUME(COND)       \
484
  do {                         \
485
    if (!(COND)) {             \
486
      __builtin_unreachable(); \
487
    }                          \
488
  } while (0)
489
#endif
490
491
#if defined(__SSSE3__)
492
#define ADA_SSSE3 1
493
#endif
494
495
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
496
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
497
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
498
#define ADA_SSE2 1
499
#endif
500
501
// AVX-512 byte/word ops + 128/256-bit vectors of AVX-512 instructions.
502
// Used for optional high-performance IP address parsing kernels.
503
#if defined(__AVX512BW__) && defined(__AVX512VL__)
504
#define ADA_AVX512 1
505
#endif
506
507
#if defined(__aarch64__) || defined(_M_ARM64)
508
#define ADA_NEON 1
509
#endif
510
511
#if defined(__loongarch_sx)
512
#define ADA_LSX 1
513
#endif
514
515
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
516
// Support RVV intrinsics v0.11 and above
517
#define ADA_RVV 1
518
#endif
519
520
#ifndef __has_cpp_attribute
521
#define ada_lifetime_bound
522
#elif __has_cpp_attribute(msvc::lifetimebound)
523
#define ada_lifetime_bound [[msvc::lifetimebound]]
524
#elif __has_cpp_attribute(clang::lifetimebound)
525
#define ada_lifetime_bound [[clang::lifetimebound]]
526
#elif __has_cpp_attribute(lifetimebound)
527
#define ada_lifetime_bound [[lifetimebound]]
528
#else
529
#define ada_lifetime_bound
530
#endif
531
532
#ifdef __cpp_lib_format
533
#if __cpp_lib_format >= 202110L
534
#include <format>
535
#define ADA_HAS_FORMAT 1
536
#endif
537
#endif
538
539
#ifndef ADA_INCLUDE_URL_PATTERN
540
#define ADA_INCLUDE_URL_PATTERN 1
541
#endif  // ADA_INCLUDE_URL_PATTERN
542
543
#endif  // ADA_COMMON_DEFS_H
544
/* end file include/ada/common_defs.h */
545
#include <cstdint>
546
547
/**
548
 * These functions are not part of our public API and may
549
 * change at any time.
550
 * @private
551
 * @namespace ada::character_sets
552
 * @brief Includes the definitions for unicode character sets.
553
 */
554
namespace ada::character_sets {
555
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
556
}  // namespace ada::character_sets
557
558
#endif  // ADA_CHARACTER_SETS_H
559
/* end file include/ada/character_sets.h */
560
/* begin file include/ada/character_sets-inl.h */
561
/**
562
 * @file character_sets-inl.h
563
 * @brief Definitions of the character sets used by unicode functions.
564
 * @author Node.js
565
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
566
 */
567
#ifndef ADA_CHARACTER_SETS_INL_H
568
#define ADA_CHARACTER_SETS_INL_H
569
570
571
/**
572
 * These functions are not part of our public API and may
573
 * change at any time.
574
 * @private
575
 */
576
namespace ada::character_sets {
577
578
constexpr char hex[1024] =
579
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
580
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
581
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
582
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
583
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
584
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
585
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
586
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
587
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
588
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
589
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
590
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
591
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
592
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
593
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
594
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
595
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
596
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
597
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
598
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
599
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
600
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
601
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
602
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
603
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
604
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
605
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
606
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
607
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
608
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
609
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
610
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
611
612
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
613
    // 00     01     02     03     04     05     06     07
614
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
615
    // 08     09     0A     0B     0C     0D     0E     0F
616
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
617
    // 10     11     12     13     14     15     16     17
618
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
619
    // 18     19     1A     1B     1C     1D     1E     1F
620
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
621
    // 20     21     22     23     24     25     26     27
622
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
623
    // 28     29     2A     2B     2C     2D     2E     2F
624
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
625
    // 30     31     32     33     34     35     36     37
626
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
627
    // 38     39     3A     3B     3C     3D     3E     3F
628
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
629
    // 40     41     42     43     44     45     46     47
630
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
631
    // 48     49     4A     4B     4C     4D     4E     4F
632
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
633
    // 50     51     52     53     54     55     56     57
634
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
635
    // 58     59     5A     5B     5C     5D     5E     5F
636
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
637
    // 60     61     62     63     64     65     66     67
638
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
639
    // 68     69     6A     6B     6C     6D     6E     6F
640
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
641
    // 70     71     72     73     74     75     76     77
642
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
643
    // 78     79     7A     7B     7C     7D     7E     7F
644
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
645
    // 80     81     82     83     84     85     86     87
646
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
647
    // 88     89     8A     8B     8C     8D     8E     8F
648
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
649
    // 90     91     92     93     94     95     96     97
650
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
651
    // 98     99     9A     9B     9C     9D     9E     9F
652
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
653
    // A0     A1     A2     A3     A4     A5     A6     A7
654
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
655
    // A8     A9     AA     AB     AC     AD     AE     AF
656
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
657
    // B0     B1     B2     B3     B4     B5     B6     B7
658
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
659
    // B8     B9     BA     BB     BC     BD     BE     BF
660
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
661
    // C0     C1     C2     C3     C4     C5     C6     C7
662
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
663
    // C8     C9     CA     CB     CC     CD     CE     CF
664
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
665
    // D0     D1     D2     D3     D4     D5     D6     D7
666
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
667
    // D8     D9     DA     DB     DC     DD     DE     DF
668
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
669
    // E0     E1     E2     E3     E4     E5     E6     E7
670
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
671
    // E8     E9     EA     EB     EC     ED     EE     EF
672
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
673
    // F0     F1     F2     F3     F4     F5     F6     F7
674
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
675
    // F8     F9     FA     FB     FC     FD     FE     FF
676
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
677
678
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
679
    // 00     01     02     03     04     05     06     07
680
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
681
    // 08     09     0A     0B     0C     0D     0E     0F
682
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
683
    // 10     11     12     13     14     15     16     17
684
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
685
    // 18     19     1A     1B     1C     1D     1E     1F
686
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
687
    // 20     21     22     23     24     25     26     27
688
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
689
    // 28     29     2A     2B     2C     2D     2E     2F
690
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
691
    // 30     31     32     33     34     35     36     37
692
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
693
    // 38     39     3A     3B     3C     3D     3E     3F
694
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
695
    // 40     41     42     43     44     45     46     47
696
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
697
    // 48     49     4A     4B     4C     4D     4E     4F
698
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
699
    // 50     51     52     53     54     55     56     57
700
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
701
    // 58     59     5A     5B     5C     5D     5E     5F
702
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
703
    // 60     61     62     63     64     65     66     67
704
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
705
    // 68     69     6A     6B     6C     6D     6E     6F
706
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
707
    // 70     71     72     73     74     75     76     77
708
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
709
    // 78     79     7A     7B     7C     7D     7E     7F
710
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
711
    // 80     81     82     83     84     85     86     87
712
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
713
    // 88     89     8A     8B     8C     8D     8E     8F
714
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
715
    // 90     91     92     93     94     95     96     97
716
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
717
    // 98     99     9A     9B     9C     9D     9E     9F
718
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
719
    // A0     A1     A2     A3     A4     A5     A6     A7
720
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
721
    // A8     A9     AA     AB     AC     AD     AE     AF
722
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
723
    // B0     B1     B2     B3     B4     B5     B6     B7
724
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
725
    // B8     B9     BA     BB     BC     BD     BE     BF
726
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
727
    // C0     C1     C2     C3     C4     C5     C6     C7
728
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
729
    // C8     C9     CA     CB     CC     CD     CE     CF
730
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
731
    // D0     D1     D2     D3     D4     D5     D6     D7
732
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
733
    // D8     D9     DA     DB     DC     DD     DE     DF
734
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
735
    // E0     E1     E2     E3     E4     E5     E6     E7
736
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
737
    // E8     E9     EA     EB     EC     ED     EE     EF
738
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
739
    // F0     F1     F2     F3     F4     F5     F6     F7
740
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
741
    // F8     F9     FA     FB     FC     FD     FE     FF
742
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
743
744
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
745
    // 00     01     02     03     04     05     06     07
746
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
747
    // 08     09     0A     0B     0C     0D     0E     0F
748
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
749
    // 10     11     12     13     14     15     16     17
750
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
751
    // 18     19     1A     1B     1C     1D     1E     1F
752
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
753
    // 20     21     22     23     24     25     26     27
754
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
755
    // 28     29     2A     2B     2C     2D     2E     2F
756
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
757
    // 30     31     32     33     34     35     36     37
758
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
759
    // 38     39     3A     3B     3C     3D     3E     3F
760
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
761
    // 40     41     42     43     44     45     46     47
762
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
763
    // 48     49     4A     4B     4C     4D     4E     4F
764
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
765
    // 50     51     52     53     54     55     56     57
766
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
767
    // 58     59     5A     5B     5C     5D     5E     5F
768
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
769
    // 60     61     62     63     64     65     66     67
770
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
771
    // 68     69     6A     6B     6C     6D     6E     6F
772
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
773
    // 70     71     72     73     74     75     76     77
774
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
775
    // 78     79     7A     7B     7C     7D     7E     7F
776
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
777
    // 80     81     82     83     84     85     86     87
778
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
779
    // 88     89     8A     8B     8C     8D     8E     8F
780
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
781
    // 90     91     92     93     94     95     96     97
782
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
783
    // 98     99     9A     9B     9C     9D     9E     9F
784
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
785
    // A0     A1     A2     A3     A4     A5     A6     A7
786
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
787
    // A8     A9     AA     AB     AC     AD     AE     AF
788
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
789
    // B0     B1     B2     B3     B4     B5     B6     B7
790
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
791
    // B8     B9     BA     BB     BC     BD     BE     BF
792
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
793
    // C0     C1     C2     C3     C4     C5     C6     C7
794
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
795
    // C8     C9     CA     CB     CC     CD     CE     CF
796
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
797
    // D0     D1     D2     D3     D4     D5     D6     D7
798
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
799
    // D8     D9     DA     DB     DC     DD     DE     DF
800
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
801
    // E0     E1     E2     E3     E4     E5     E6     E7
802
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
803
    // E8     E9     EA     EB     EC     ED     EE     EF
804
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
805
    // F0     F1     F2     F3     F4     F5     F6     F7
806
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
807
    // F8     F9     FA     FB     FC     FD     FE     FF
808
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
809
810
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
811
    // 00     01     02     03     04     05     06     07
812
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
813
    // 08     09     0A     0B     0C     0D     0E     0F
814
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
815
    // 10     11     12     13     14     15     16     17
816
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
817
    // 18     19     1A     1B     1C     1D     1E     1F
818
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
819
    // 20     21     22     23     24     25     26     27
820
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
821
    // 28     29     2A     2B     2C     2D     2E     2F
822
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
823
    // 30     31     32     33     34     35     36     37
824
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
825
    // 38     39     3A     3B     3C     3D     3E     3F
826
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
827
    // 40     41     42     43     44     45     46     47
828
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
829
    // 48     49     4A     4B     4C     4D     4E     4F
830
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
831
    // 50     51     52     53     54     55     56     57
832
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
833
    // 58     59     5A     5B     5C     5D     5E     5F
834
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
835
    // 60     61     62     63     64     65     66     67
836
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
837
    // 68     69     6A     6B     6C     6D     6E     6F
838
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
839
    // 70     71     72     73     74     75     76     77
840
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
841
    // 78     79     7A     7B     7C     7D     7E     7F
842
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
843
    // 80     81     82     83     84     85     86     87
844
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
845
    // 88     89     8A     8B     8C     8D     8E     8F
846
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
847
    // 90     91     92     93     94     95     96     97
848
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
849
    // 98     99     9A     9B     9C     9D     9E     9F
850
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
851
    // A0     A1     A2     A3     A4     A5     A6     A7
852
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
853
    // A8     A9     AA     AB     AC     AD     AE     AF
854
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
855
    // B0     B1     B2     B3     B4     B5     B6     B7
856
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
857
    // B8     B9     BA     BB     BC     BD     BE     BF
858
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
859
    // C0     C1     C2     C3     C4     C5     C6     C7
860
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
861
    // C8     C9     CA     CB     CC     CD     CE     CF
862
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
863
    // D0     D1     D2     D3     D4     D5     D6     D7
864
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
865
    // D8     D9     DA     DB     DC     DD     DE     DF
866
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
867
    // E0     E1     E2     E3     E4     E5     E6     E7
868
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
869
    // E8     E9     EA     EB     EC     ED     EE     EF
870
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
871
    // F0     F1     F2     F3     F4     F5     F6     F7
872
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
873
    // F8     F9     FA     FB     FC     FD     FE     FF
874
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
875
876
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
877
    // 00     01     02     03     04     05     06     07
878
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
879
    // 08     09     0A     0B     0C     0D     0E     0F
880
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
881
    // 10     11     12     13     14     15     16     17
882
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
883
    // 18     19     1A     1B     1C     1D     1E     1F
884
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
885
    // 20     21     22     23     24     25     26     27
886
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
887
    // 28     29     2A     2B     2C     2D     2E     2F
888
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
889
    // 30     31     32     33     34     35     36     37
890
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
891
    // 38     39     3A     3B     3C     3D     3E     3F
892
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
893
    // 40     41     42     43     44     45     46     47
894
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
895
    // 48     49     4A     4B     4C     4D     4E     4F
896
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
897
    // 50     51     52     53     54     55     56     57
898
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
899
    // 58     59     5A     5B     5C     5D     5E     5F
900
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
901
    // 60     61     62     63     64     65     66     67
902
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
903
    // 68     69     6A     6B     6C     6D     6E     6F
904
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
905
    // 70     71     72     73     74     75     76     77
906
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
907
    // 78     79     7A     7B     7C     7D     7E     7F
908
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
909
    // 80     81     82     83     84     85     86     87
910
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
911
    // 88     89     8A     8B     8C     8D     8E     8F
912
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
913
    // 90     91     92     93     94     95     96     97
914
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
915
    // 98     99     9A     9B     9C     9D     9E     9F
916
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
917
    // A0     A1     A2     A3     A4     A5     A6     A7
918
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
919
    // A8     A9     AA     AB     AC     AD     AE     AF
920
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
921
    // B0     B1     B2     B3     B4     B5     B6     B7
922
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
923
    // B8     B9     BA     BB     BC     BD     BE     BF
924
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
925
    // C0     C1     C2     C3     C4     C5     C6     C7
926
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
927
    // C8     C9     CA     CB     CC     CD     CE     CF
928
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
929
    // D0     D1     D2     D3     D4     D5     D6     D7
930
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
931
    // D8     D9     DA     DB     DC     DD     DE     DF
932
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
933
    // E0     E1     E2     E3     E4     E5     E6     E7
934
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
935
    // E8     E9     EA     EB     EC     ED     EE     EF
936
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
937
    // F0     F1     F2     F3     F4     F5     F6     F7
938
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
939
    // F8     F9     FA     FB     FC     FD     FE     FF
940
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
941
942
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
943
    // 00     01     02     03     04     05     06     07
944
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
945
    // 08     09     0A     0B     0C     0D     0E     0F
946
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
947
    // 10     11     12     13     14     15     16     17
948
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
949
    // 18     19     1A     1B     1C     1D     1E     1F
950
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
951
    // 20     21     22     23     24     25     26     27
952
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
953
    // 28     29     2A     2B     2C     2D     2E     2F
954
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
955
    // 30     31     32     33     34     35     36     37
956
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
957
    // 38     39     3A     3B     3C     3D     3E     3F
958
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
959
    // 40     41     42     43     44     45     46     47
960
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
961
    // 48     49     4A     4B     4C     4D     4E     4F
962
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
963
    // 50     51     52     53     54     55     56     57
964
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
965
    // 58     59     5A     5B     5C     5D     5E     5F
966
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
967
    // 60     61     62     63     64     65     66     67
968
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
969
    // 68     69     6A     6B     6C     6D     6E     6F
970
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
971
    // 70     71     72     73     74     75     76     77
972
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
973
    // 78     79     7A     7B     7C     7D     7E     7F
974
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
975
    // 80     81     82     83     84     85     86     87
976
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
977
    // 88     89     8A     8B     8C     8D     8E     8F
978
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
979
    // 90     91     92     93     94     95     96     97
980
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
981
    // 98     99     9A     9B     9C     9D     9E     9F
982
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
983
    // A0     A1     A2     A3     A4     A5     A6     A7
984
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
985
    // A8     A9     AA     AB     AC     AD     AE     AF
986
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
987
    // B0     B1     B2     B3     B4     B5     B6     B7
988
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
989
    // B8     B9     BA     BB     BC     BD     BE     BF
990
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
991
    // C0     C1     C2     C3     C4     C5     C6     C7
992
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
993
    // C8     C9     CA     CB     CC     CD     CE     CF
994
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
995
    // D0     D1     D2     D3     D4     D5     D6     D7
996
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
997
    // D8     D9     DA     DB     DC     DD     DE     DF
998
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
999
    // E0     E1     E2     E3     E4     E5     E6     E7
1000
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1001
    // E8     E9     EA     EB     EC     ED     EE     EF
1002
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1003
    // F0     F1     F2     F3     F4     F5     F6     F7
1004
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1005
    // F8     F9     FA     FB     FC     FD     FE     FF
1006
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1007
1008
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1009
    // 00     01     02     03     04     05     06     07
1010
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1011
    // 08     09     0A     0B     0C     0D     0E     0F
1012
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1013
    // 10     11     12     13     14     15     16     17
1014
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1015
    // 18     19     1A     1B     1C     1D     1E     1F
1016
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1017
    // 20     21     22     23     24     25     26     27
1018
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1019
    // 28     29     2A     2B     2C     2D     2E     2F
1020
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1021
    // 30     31     32     33     34     35     36     37
1022
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1023
    // 38     39     3A     3B     3C     3D     3E     3F
1024
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1025
    // 40     41     42     43     44     45     46     47
1026
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1027
    // 48     49     4A     4B     4C     4D     4E     4F
1028
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1029
    // 50     51     52     53     54     55     56     57
1030
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1031
    // 58     59     5A     5B     5C     5D     5E     5F
1032
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1033
    // 60     61     62     63     64     65     66     67
1034
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1035
    // 68     69     6A     6B     6C     6D     6E     6F
1036
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1037
    // 70     71     72     73     74     75     76     77
1038
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1039
    // 78     79     7A     7B     7C     7D     7E     7F
1040
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1041
    // 80     81     82     83     84     85     86     87
1042
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1043
    // 88     89     8A     8B     8C     8D     8E     8F
1044
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1045
    // 90     91     92     93     94     95     96     97
1046
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1047
    // 98     99     9A     9B     9C     9D     9E     9F
1048
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1049
    // A0     A1     A2     A3     A4     A5     A6     A7
1050
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1051
    // A8     A9     AA     AB     AC     AD     AE     AF
1052
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1053
    // B0     B1     B2     B3     B4     B5     B6     B7
1054
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1055
    // B8     B9     BA     BB     BC     BD     BE     BF
1056
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1057
    // C0     C1     C2     C3     C4     C5     C6     C7
1058
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1059
    // C8     C9     CA     CB     CC     CD     CE     CF
1060
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1061
    // D0     D1     D2     D3     D4     D5     D6     D7
1062
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1063
    // D8     D9     DA     DB     DC     DD     DE     DF
1064
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1065
    // E0     E1     E2     E3     E4     E5     E6     E7
1066
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1067
    // E8     E9     EA     EB     EC     ED     EE     EF
1068
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1069
    // F0     F1     F2     F3     F4     F5     F6     F7
1070
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1071
    // F8     F9     FA     FB     FC     FD     FE     FF
1072
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1073
1074
13.0M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1075
13.0M
  return !!(a[i >> 3] & (1 << (i & 7)));
1076
13.0M
}
1077
1078
}  // namespace ada::character_sets
1079
1080
#endif  // ADA_CHARACTER_SETS_INL_H
1081
/* end file include/ada/character_sets-inl.h */
1082
/* begin file include/ada/checkers-inl.h */
1083
/**
1084
 * @file checkers-inl.h
1085
 * @brief Definitions for URL specific checkers used within Ada.
1086
 */
1087
#ifndef ADA_CHECKERS_INL_H
1088
#define ADA_CHECKERS_INL_H
1089
1090
#include <bit>
1091
#include <cstdint>
1092
#include <cstring>
1093
#include <string_view>
1094
/* begin file include/ada/checkers.h */
1095
/**
1096
 * @file checkers.h
1097
 * @brief Declarations for URL specific checkers used within Ada.
1098
 */
1099
#ifndef ADA_CHECKERS_H
1100
#define ADA_CHECKERS_H
1101
1102
1103
#include <cstring>
1104
#include <string_view>
1105
1106
/**
1107
 * These functions are not part of our public API and may
1108
 * change at any time.
1109
 * @private
1110
 * @namespace ada::checkers
1111
 * @brief Includes the definitions for validation functions
1112
 */
1113
namespace ada::checkers {
1114
1115
/**
1116
 * @private
1117
 * Assuming that x is an ASCII letter, this function returns the lower case
1118
 * equivalent.
1119
 * @details More likely to be inlined by the compiler and constexpr.
1120
 */
1121
constexpr char to_lower(char x) noexcept;
1122
1123
/**
1124
 * @private
1125
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1126
 * but more likely to be inlined by the compiler.
1127
 *
1128
 * @attention std::isalpha is not constexpr generally.
1129
 */
1130
constexpr bool is_alpha(char x) noexcept;
1131
1132
/**
1133
 * @private
1134
 * Check whether a string starts with 0x or 0X. The function is only
1135
 * safe if input.size() >=2.
1136
 *
1137
 * @see has_hex_prefix
1138
 */
1139
constexpr bool has_hex_prefix_unsafe(std::string_view input);
1140
/**
1141
 * @private
1142
 * Check whether a string starts with 0x or 0X.
1143
 */
1144
constexpr bool has_hex_prefix(std::string_view input);
1145
1146
/**
1147
 * @private
1148
 * Check whether x is an ASCII digit. More likely to be inlined than
1149
 * std::isdigit.
1150
 */
1151
constexpr bool is_digit(char x) noexcept;
1152
1153
/**
1154
 * @private
1155
 * @details A string starts with a Windows drive letter if all of the following
1156
 * are true:
1157
 *
1158
 *   - its length is greater than or equal to 2
1159
 *   - its first two code points are a Windows drive letter
1160
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1161
 * (?), or U+0023 (#).
1162
 *
1163
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1164
 */
1165
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1166
1167
/**
1168
 * @private
1169
 * @details A normalized Windows drive letter is a Windows drive letter of which
1170
 * the second code point is U+003A (:).
1171
 */
1172
inline constexpr bool is_normalized_windows_drive_letter(
1173
    std::string_view input) noexcept;
1174
1175
/**
1176
 * @private
1177
 * Returns true if an input is an ipv4 address. It is assumed that the string
1178
 * does not contain uppercase ASCII characters (the input should have been
1179
 * lowered cased before calling this function) and is not empty.
1180
 */
1181
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1182
1183
/**
1184
 * @private
1185
 * Returns a bitset. If the first bit is set, then at least one character needs
1186
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1187
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1188
 * character.
1189
 */
1190
ada_really_inline constexpr uint8_t path_signature(
1191
    std::string_view input) noexcept;
1192
1193
/**
1194
 * @private
1195
 * Returns true if the length of the domain name and its labels are according to
1196
 * the specifications. The length of the domain must be 255 octets (253
1197
 * characters not including the last 2 which are the empty label reserved at the
1198
 * end). When the empty label is included (a dot at the end), the domain name
1199
 * can have 254 characters. The length of a label must be at least 1 and at most
1200
 * 63 characters.
1201
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1202
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1203
 */
1204
ada_really_inline constexpr bool verify_dns_length(
1205
    std::string_view input) noexcept;
1206
1207
/**
1208
 * @private
1209
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1210
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1211
 * to indicate failure (caller should fall back to general parser).
1212
 * This is optimized for the common case where the input is a well-formed
1213
 * decimal IPv4 address with exactly 4 octets.
1214
 */
1215
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1216
1217
/**
1218
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1219
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1220
 */
1221
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1222
1223
}  // namespace ada::checkers
1224
1225
#endif  // ADA_CHECKERS_H
1226
/* end file include/ada/checkers.h */
1227
1228
#if defined(ADA_AVX512)
1229
#include <immintrin.h>
1230
#endif
1231
1232
namespace ada::checkers {
1233
1234
0
constexpr bool has_hex_prefix_unsafe(std::string_view input) {
1235
0
  // This is actually efficient code, see has_hex_prefix for the assembly.
1236
0
  constexpr bool is_little_endian = std::endian::native == std::endian::little;
1237
0
  constexpr uint16_t word0x = 0x7830;
1238
0
  uint16_t two_first_bytes =
1239
0
      static_cast<uint16_t>(input[0]) |
1240
0
      static_cast<uint16_t>((static_cast<uint16_t>(input[1]) << 8));
1241
0
  if constexpr (is_little_endian) {
1242
0
    two_first_bytes |= 0x2000;
1243
0
  } else {
1244
0
    two_first_bytes |= 0x020;
1245
0
  }
1246
0
  return two_first_bytes == word0x;
1247
0
}
1248
1249
0
constexpr bool has_hex_prefix(std::string_view input) {
1250
0
  return input.size() >= 2 && has_hex_prefix_unsafe(input);
1251
0
}
1252
1253
346k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1254
1255
2.53M
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1256
1257
1.30M
constexpr bool is_alpha(char x) noexcept {
1258
1.30M
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1259
1.30M
}
1260
1261
55.7k
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1262
55.7k
  return input.size() >= 2 &&
1263
44.8k
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1264
7.75k
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1265
4.38k
                                  input[2] == '?' || input[2] == '#'));
1266
55.7k
}
1267
1268
constexpr bool is_normalized_windows_drive_letter(
1269
17.9k
    std::string_view input) noexcept {
1270
17.9k
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1271
17.9k
}
1272
1273
namespace detail {
1274
1275
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1276
ada_really_inline uint64_t
1277
280k
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1278
280k
  uint32_t ipv4 = 0;
1279
468k
  for (int i = 0; i < 4; ++i) {
1280
422k
    if (p == pend) [[unlikely]] {
1281
95
      return ipv4_fast_fail;
1282
95
    }
1283
422k
    uint32_t val;
1284
422k
    char c = *p;
1285
422k
    if (c >= '0' && c <= '9') [[likely]] {
1286
226k
      val = static_cast<uint32_t>(c - '0');
1287
226k
      ++p;
1288
226k
    } else {
1289
196k
      return ipv4_fast_fail;
1290
196k
    }
1291
226k
    if (p < pend) {
1292
185k
      c = *p;
1293
185k
      if (c >= '0' && c <= '9') {
1294
87.9k
        if (val == 0) [[unlikely]] {
1295
11.2k
          return ipv4_fast_fail;
1296
11.2k
        }
1297
76.6k
        val = val * 10u + static_cast<uint32_t>(c - '0');
1298
76.6k
        ++p;
1299
76.6k
        if (p < pend) {
1300
76.1k
          c = *p;
1301
76.1k
          if (c >= '0' && c <= '9') {
1302
73.4k
            val = val * 10u + static_cast<uint32_t>(c - '0');
1303
73.4k
            ++p;
1304
73.4k
            if (val > 255u) [[unlikely]] {
1305
5.54k
              return ipv4_fast_fail;
1306
5.54k
            }
1307
73.4k
          }
1308
76.1k
        }
1309
76.6k
      }
1310
185k
    }
1311
209k
    ipv4 = (ipv4 << 8) | val;
1312
209k
    if (i < 3) {
1313
164k
      if (p == pend || *p != '.') [[unlikely]] {
1314
21.5k
        return ipv4_fast_fail;
1315
21.5k
      }
1316
142k
      ++p;
1317
142k
    }
1318
209k
  }
1319
45.3k
  if (p != pend) {
1320
3.37k
    if (p == pend - 1 && *p == '.') {
1321
396
      return ipv4;
1322
396
    }
1323
2.98k
    return ipv4_fast_fail;
1324
3.37k
  }
1325
41.9k
  return ipv4;
1326
45.3k
}
1327
1328
#if defined(ADA_AVX512)
1329
// After SIMD validation: fewer rejection branches on convert.
1330
ada_really_inline uint64_t
1331
parse_ipv4_decimal_trusted(const char* p, const char* pend) noexcept {
1332
  uint32_t ipv4 = 0;
1333
  for (int i = 0; i < 4; ++i) {
1334
    uint32_t val = static_cast<uint32_t>(*p - '0');
1335
    ++p;
1336
    if (p < pend && static_cast<unsigned char>(*p - '0') <= 9) {
1337
      if (val == 0) [[unlikely]] {
1338
        return ipv4_fast_fail;
1339
      }
1340
      val = val * 10u + static_cast<uint32_t>(*p - '0');
1341
      ++p;
1342
      if (p < pend && static_cast<unsigned char>(*p - '0') <= 9) {
1343
        val = val * 10u + static_cast<uint32_t>(*p - '0');
1344
        ++p;
1345
        if (val > 255u) [[unlikely]] {
1346
          return ipv4_fast_fail;
1347
        }
1348
      }
1349
    }
1350
    ipv4 = (ipv4 << 8) | val;
1351
    if (i < 3) {
1352
      ++p;  // trusted '.'
1353
    }
1354
  }
1355
  return ipv4;  // trailing-dot already accounted for by caller via pend
1356
}
1357
1358
// AVX-512 pure-decimal IPv4 (Lemire/Mula-style masked load + parallel checks).
1359
// No over-read of the source string. Wins when the binary is built with
1360
// -mavx512bw -mavx512vl (or -march that enables them).
1361
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1362
                                                 size_t len) noexcept {
1363
  const __mmask16 live = static_cast<__mmask16>((1u << len) - 1u);
1364
  const __m128i input =
1365
      _mm_maskz_loadu_epi8(live, reinterpret_cast<const void*>(data));
1366
  const __mmask16 is_dot =
1367
      _mm_mask_cmpeq_epi8_mask(live, input, _mm_set1_epi8('.'));
1368
  const __m128i shifted = _mm_sub_epi8(input, _mm_set1_epi8('0'));
1369
  const __mmask16 is_digit =
1370
      _mm_mask_cmplt_epu8_mask(live, shifted, _mm_set1_epi8(10));
1371
  if ((is_digit | is_dot) != live) {
1372
    return ipv4_fast_fail;
1373
  }
1374
  const unsigned dot_count =
1375
      static_cast<unsigned>(_mm_popcnt_u32(static_cast<unsigned>(is_dot)));
1376
  size_t effective_len = len;
1377
  if (dot_count == 3) {
1378
    // ok
1379
  } else if (dot_count == 4 && data[len - 1] == '.') {
1380
    effective_len = len - 1;  // strip trailing dot for convert
1381
  } else {
1382
    return ipv4_fast_fail;
1383
  }
1384
  // Convert from a tiny stack copy so trusted peeks stay in-bounds.
1385
  alignas(16) char buf[16]{};
1386
  std::memcpy(buf, data, effective_len);
1387
  return parse_ipv4_decimal_trusted(buf, buf + effective_len);
1388
}
1389
#endif  // ADA_AVX512
1390
1391
}  // namespace detail
1392
1393
/**
1394
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1395
 * Accepts an optional single trailing dot.
1396
 *
1397
 * On AVX-512BW+VL targets, uses a masked-load SIMD kernel (no source
1398
 * over-read) inspired by Lemire/Mula. Otherwise uses an unrolled scalar path
1399
 * (typically faster than SSE2/NEON pre-validation for these 7-16 byte hosts).
1400
 */
1401
ada_really_inline uint64_t
1402
607k
try_parse_ipv4_fast(std::string_view input) noexcept {
1403
607k
  const size_t len = input.size();
1404
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1405
607k
  if (len < 7 || len > 16) [[unlikely]] {
1406
327k
    return ipv4_fast_fail;
1407
327k
  }
1408
280k
  const char* data = input.data();
1409
1410
#if defined(ADA_AVX512)
1411
  return detail::try_parse_ipv4_avx512(data, len);
1412
#else
1413
280k
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1414
607k
#endif
1415
607k
}
1416
1417
}  // namespace ada::checkers
1418
1419
#endif  // ADA_CHECKERS_INL_H
1420
/* end file include/ada/checkers-inl.h */
1421
/* begin file include/ada/log.h */
1422
/**
1423
 * @file log.h
1424
 * @brief Includes the definitions for logging.
1425
 * @private Excluded from docs through the doxygen file.
1426
 */
1427
#ifndef ADA_LOG_H
1428
#define ADA_LOG_H
1429
1430
// To enable logging, set ADA_LOGGING to 1:
1431
#ifndef ADA_LOGGING
1432
#define ADA_LOGGING 0
1433
#endif
1434
1435
#if ADA_LOGGING
1436
#include <iostream>
1437
#endif  // ADA_LOGGING
1438
1439
namespace ada {
1440
1441
/**
1442
 * Log a message. If you want to have no overhead when logging is disabled, use
1443
 * the ada_log macro.
1444
 * @private
1445
 */
1446
template <typename... Args>
1447
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1448
#if ADA_LOGGING
1449
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1450
#endif  // ADA_LOGGING
1451
}
1452
}  // namespace ada
1453
1454
#if ADA_LOGGING
1455
#ifndef ada_log
1456
#define ada_log(...)       \
1457
  do {                     \
1458
    ada::log(__VA_ARGS__); \
1459
  } while (0)
1460
#endif  // ada_log
1461
#else
1462
#define ada_log(...)
1463
#endif  // ADA_LOGGING
1464
1465
#endif  // ADA_LOG_H
1466
/* end file include/ada/log.h */
1467
/* begin file include/ada/encoding_type.h */
1468
/**
1469
 * @file encoding_type.h
1470
 * @brief Character encoding type definitions.
1471
 *
1472
 * Defines the encoding types supported for URL processing.
1473
 *
1474
 * @see https://encoding.spec.whatwg.org/
1475
 */
1476
#ifndef ADA_ENCODING_TYPE_H
1477
#define ADA_ENCODING_TYPE_H
1478
1479
#include <string>
1480
1481
namespace ada {
1482
1483
/**
1484
 * @brief Character encoding types for URL processing.
1485
 *
1486
 * Specifies the character encoding used for percent-decoding and other
1487
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1488
 *
1489
 * @see https://encoding.spec.whatwg.org/#encodings
1490
 */
1491
enum class encoding_type {
1492
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1493
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1494
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1495
};
1496
1497
/**
1498
 * Converts an encoding_type to its string representation.
1499
 * @param type The encoding type to convert.
1500
 * @return A string view of the encoding name.
1501
 */
1502
ada_warn_unused std::string_view to_string(encoding_type type);
1503
1504
}  // namespace ada
1505
1506
#endif  // ADA_ENCODING_TYPE_H
1507
/* end file include/ada/encoding_type.h */
1508
/* begin file include/ada/helpers.h */
1509
/**
1510
 * @file helpers.h
1511
 * @brief Definitions for helper functions used within Ada.
1512
 */
1513
#ifndef ADA_HELPERS_H
1514
#define ADA_HELPERS_H
1515
1516
/* begin file include/ada/url_base.h */
1517
/**
1518
 * @file url_base.h
1519
 * @brief Base class and common definitions for URL types.
1520
 *
1521
 * This file defines the `url_base` abstract base class from which both
1522
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1523
 * enumerations like `url_host_type`.
1524
 */
1525
#ifndef ADA_URL_BASE_H
1526
#define ADA_URL_BASE_H
1527
1528
/* begin file include/ada/scheme.h */
1529
/**
1530
 * @file scheme.h
1531
 * @brief URL scheme type definitions and utilities.
1532
 *
1533
 * This header defines the URL scheme types (http, https, etc.) and provides
1534
 * functions to identify special schemes and their default ports according
1535
 * to the WHATWG URL Standard.
1536
 *
1537
 * @see https://url.spec.whatwg.org/#special-scheme
1538
 */
1539
#ifndef ADA_SCHEME_H
1540
#define ADA_SCHEME_H
1541
1542
1543
#include <string>
1544
1545
/**
1546
 * @namespace ada::scheme
1547
 * @brief URL scheme utilities and constants.
1548
 *
1549
 * Provides functions for working with URL schemes, including identification
1550
 * of special schemes and retrieval of default port numbers.
1551
 */
1552
namespace ada::scheme {
1553
1554
/**
1555
 * @brief Enumeration of URL scheme types.
1556
 *
1557
 * Special schemes have specific parsing rules and default ports.
1558
 * Using an enum allows efficient scheme comparisons without string operations.
1559
 *
1560
 * Default ports:
1561
 * - HTTP: 80
1562
 * - HTTPS: 443
1563
 * - WS: 80
1564
 * - WSS: 443
1565
 * - FTP: 21
1566
 * - FILE: (none)
1567
 */
1568
enum type : uint8_t {
1569
  HTTP = 0,        /**< http:// scheme (port 80) */
1570
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1571
  HTTPS = 2,       /**< https:// scheme (port 443) */
1572
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1573
  FTP = 4,         /**< ftp:// scheme (port 21) */
1574
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1575
  FILE = 6         /**< file:// scheme (no default port) */
1576
};
1577
1578
/**
1579
 * Checks if a scheme string is a special scheme.
1580
 * @param scheme The scheme string to check (e.g., "http", "https").
1581
 * @return `true` if the scheme is special, `false` otherwise.
1582
 * @see https://url.spec.whatwg.org/#special-scheme
1583
 */
1584
ada_really_inline constexpr bool is_special(std::string_view scheme);
1585
1586
/**
1587
 * Returns the default port for a special scheme string.
1588
 * @param scheme The scheme string (e.g., "http", "https").
1589
 * @return The default port number, or 0 if not a special scheme.
1590
 * @see https://url.spec.whatwg.org/#special-scheme
1591
 */
1592
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1593
1594
/**
1595
 * Returns the default port for a scheme type.
1596
 * @param type The scheme type enum value.
1597
 * @return The default port number, or 0 if not applicable.
1598
 * @see https://url.spec.whatwg.org/#special-scheme
1599
 */
1600
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1601
1602
/**
1603
 * Converts a scheme string to its type enum.
1604
 * @param scheme The scheme string to convert.
1605
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1606
 */
1607
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1608
1609
}  // namespace ada::scheme
1610
1611
#endif  // ADA_SCHEME_H
1612
/* end file include/ada/scheme.h */
1613
1614
#include <string>
1615
#include <string_view>
1616
1617
namespace ada {
1618
1619
/**
1620
 * @brief Enum representing the type of host in a URL.
1621
 *
1622
 * Used to distinguish between regular domain names, IPv4 addresses,
1623
 * and IPv6 addresses for proper parsing and serialization.
1624
 */
1625
enum url_host_type : uint8_t {
1626
  /** Regular domain name (e.g., "www.example.com") */
1627
  DEFAULT = 0,
1628
  /** IPv4 address (e.g., "127.0.0.1") */
1629
  IPV4 = 1,
1630
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1631
  IPV6 = 2,
1632
};
1633
1634
/**
1635
 * @brief Abstract base class for URL representations.
1636
 *
1637
 * The `url_base` class provides the common interface and state shared by
1638
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1639
 * like validity status and scheme type, but delegates component storage and
1640
 * access to derived classes.
1641
 *
1642
 * @note This is an abstract class and cannot be instantiated directly.
1643
 *       Use `ada::url` or `ada::url_aggregator` instead.
1644
 *
1645
 * @see url
1646
 * @see url_aggregator
1647
 */
1648
struct url_base {
1649
4.08M
  virtual ~url_base() = default;
1650
1651
  /**
1652
   * Indicates whether the URL was successfully parsed.
1653
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1654
   */
1655
  bool is_valid{true};
1656
1657
  /**
1658
   * Indicates whether the URL has an opaque path (non-hierarchical).
1659
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1660
   */
1661
  bool has_opaque_path{false};
1662
1663
  /**
1664
   * The type of the URL's host (domain, IPv4, or IPv6).
1665
   */
1666
  url_host_type host_type = url_host_type::DEFAULT;
1667
1668
  /**
1669
   * @private
1670
   * Internal representation of the URL's scheme type.
1671
   */
1672
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1673
1674
  /**
1675
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1676
   * Special schemes have specific parsing rules and default ports.
1677
   * @return `true` if the scheme is special, `false` otherwise.
1678
   */
1679
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1680
1681
  /**
1682
   * Returns the URL's origin (scheme + host + port for special URLs).
1683
   * @return A newly allocated string containing the serialized origin.
1684
   * @see https://url.spec.whatwg.org/#concept-url-origin
1685
   */
1686
  [[nodiscard]] virtual std::string get_origin() const = 0;
1687
1688
  /**
1689
   * Validates whether the hostname is a valid domain according to RFC 1034.
1690
   * Checks that the domain and its labels have valid lengths.
1691
   * @return `true` if the domain is valid, `false` otherwise.
1692
   */
1693
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1694
1695
  /**
1696
   * @private
1697
   * Returns the default port for special schemes (e.g., 443 for https).
1698
   * Returns 0 for file:// URLs or non-special schemes.
1699
   */
1700
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1701
1702
  /**
1703
   * @private
1704
   * Returns the default port for the URL's scheme, or 0 if none.
1705
   */
1706
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1707
1708
  /**
1709
   * @private
1710
   * Parses a port number from the input string.
1711
   * @param view The string containing the port to parse.
1712
   * @param check_trailing_content Whether to validate no trailing characters.
1713
   * @return Number of bytes consumed on success, 0 on failure.
1714
   */
1715
  virtual size_t parse_port(std::string_view view,
1716
                            bool check_trailing_content) = 0;
1717
1718
  /** @private */
1719
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1720
0
    return this->parse_port(view, false);
1721
0
  }
1722
1723
  /**
1724
   * Returns a JSON string representation of this URL for debugging.
1725
   * @return A JSON-formatted string with URL information.
1726
   */
1727
  [[nodiscard]] virtual std::string to_string() const = 0;
1728
1729
  /** @private */
1730
  virtual inline void clear_pathname() = 0;
1731
1732
  /** @private */
1733
  virtual inline void clear_search() = 0;
1734
1735
  /** @private */
1736
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1737
1738
  /** @private */
1739
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1740
1741
};  // url_base
1742
1743
}  // namespace ada
1744
1745
#endif
1746
/* end file include/ada/url_base.h */
1747
1748
#include <string>
1749
#include <string_view>
1750
#include <optional>
1751
1752
#if ADA_DEVELOPMENT_CHECKS
1753
#include <iostream>
1754
#endif  // ADA_DEVELOPMENT_CHECKS
1755
1756
/**
1757
 * These functions are not part of our public API and may
1758
 * change at any time.
1759
 *
1760
 * @private
1761
 * @namespace ada::helpers
1762
 * @brief Includes the definitions for helper functions
1763
 */
1764
namespace ada::helpers {
1765
1766
/**
1767
 * @private
1768
 */
1769
template <typename out_iter>
1770
void encode_json(std::string_view view, out_iter out);
1771
1772
/**
1773
 * @private
1774
 * This function is used to prune a fragment from a url, and returning the
1775
 * removed string if input has fragment.
1776
 *
1777
 * @details prune_hash seeks the first '#' and returns everything after it
1778
 * as a string_view, and modifies (in place) the input so that it points at
1779
 * everything before the '#'. If no '#' is found, the input is left unchanged
1780
 * and std::nullopt is returned.
1781
 *
1782
 * @attention The function is non-allocating and it does not throw.
1783
 * @returns Note that the returned string_view might be empty!
1784
 */
1785
ada_really_inline std::optional<std::string_view> prune_hash(
1786
    std::string_view& input) noexcept;
1787
1788
/**
1789
 * @private
1790
 * Defined by the URL specification, shorten a URLs paths.
1791
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1792
 * @returns Returns true if path is shortened.
1793
 */
1794
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1795
1796
/**
1797
 * @private
1798
 * Defined by the URL specification, shorten a URLs paths.
1799
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1800
 * @returns Returns true if path is shortened.
1801
 */
1802
ada_really_inline bool shorten_path(std::string_view& path,
1803
                                    ada::scheme::type type);
1804
1805
/**
1806
 * @private
1807
 *
1808
 * Parse the path from the provided input and append to the existing
1809
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1810
 * is the user's responsibility to check.
1811
 *
1812
 * The input is expected to be UTF-8.
1813
 *
1814
 * @see https://url.spec.whatwg.org/
1815
 */
1816
ada_really_inline void parse_prepared_path(std::string_view input,
1817
                                           ada::scheme::type type,
1818
                                           std::string& path);
1819
1820
/**
1821
 * @private
1822
 * Remove and mutate all ASCII tab or newline characters from an input.
1823
 */
1824
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1825
1826
/**
1827
 * @private
1828
 * Return the substring from input going from index pos to the end.
1829
 */
1830
ada_really_inline constexpr std::string_view substring(std::string_view input,
1831
                                                       size_t pos);
1832
1833
/**
1834
 * @private
1835
 * Returns true if the string_view points within the string.
1836
 */
1837
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1838
1839
/**
1840
 * @private
1841
 * Return the substring from input going from index pos1 to the pos2 (non
1842
 * included). The length of the substring is pos2 - pos1.
1843
 */
1844
ada_really_inline constexpr std::string_view substring(std::string_view input,
1845
                                                       size_t pos1,
1846
2.57M
                                                       size_t pos2) {
1847
#if ADA_DEVELOPMENT_CHECKS
1848
  if (pos2 < pos1) {
1849
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1850
              << std::endl;
1851
    abort();
1852
  }
1853
#endif
1854
2.57M
  return input.substr(pos1, pos2 - pos1);
1855
2.57M
}
1856
1857
/**
1858
 * @private
1859
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1860
 * input.size(). This function cannot throw.
1861
 */
1862
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1863
1864
/**
1865
 * @private
1866
 * Returns a host's delimiter location depending on the state of the instance,
1867
 * and whether a colon was found outside brackets. Used by the host parser.
1868
 */
1869
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1870
    bool is_special, std::string_view& view) noexcept;
1871
1872
/**
1873
 * @private
1874
 * Removes leading and trailing C0 control and whitespace characters from
1875
 * string.
1876
 */
1877
void trim_c0_whitespace(std::string_view& input) noexcept;
1878
1879
/**
1880
 * @private
1881
 * @see
1882
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1883
 */
1884
template <class url_type>
1885
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1886
1887
/**
1888
 * @private
1889
 * Finds the delimiter of a view in authority state.
1890
 */
1891
ada_really_inline size_t
1892
find_authority_delimiter_special(std::string_view view) noexcept;
1893
1894
/**
1895
 * @private
1896
 * Finds the delimiter of a view in authority state.
1897
 */
1898
ada_really_inline size_t
1899
find_authority_delimiter(std::string_view view) noexcept;
1900
1901
/**
1902
 * @private
1903
 */
1904
template <typename T, typename... Args>
1905
506k
inline void inner_concat(std::string& buffer, T t) {
1906
506k
  buffer.append(t);
1907
506k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1905
60.3k
inline void inner_concat(std::string& buffer, T t) {
1906
60.3k
  buffer.append(t);
1907
60.3k
}
void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Line
Count
Source
1905
414k
inline void inner_concat(std::string& buffer, T t) {
1906
414k
  buffer.append(t);
1907
414k
}
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1905
31.4k
inline void inner_concat(std::string& buffer, T t) {
1906
31.4k
  buffer.append(t);
1907
31.4k
}
1908
1909
/**
1910
 * @private
1911
 */
1912
template <typename T, typename... Args>
1913
558k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
558k
  buffer.append(t);
1915
558k
  return inner_concat(buffer, args...);
1916
558k
}
void ada::helpers::inner_concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1913
60.3k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
60.3k
  buffer.append(t);
1915
60.3k
  return inner_concat(buffer, args...);
1916
60.3k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1913
20.5k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
20.5k
  buffer.append(t);
1915
20.5k
  return inner_concat(buffer, args...);
1916
20.5k
}
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1913
404k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
404k
  buffer.append(t);
1915
404k
  return inner_concat(buffer, args...);
1916
404k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Line
Count
Source
1913
10.5k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
10.5k
  buffer.append(t);
1915
10.5k
  return inner_concat(buffer, args...);
1916
10.5k
}
void ada::helpers::inner_concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1913
31.4k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
31.4k
  buffer.append(t);
1915
31.4k
  return inner_concat(buffer, args...);
1916
31.4k
}
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1913
31.4k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1914
31.4k
  buffer.append(t);
1915
31.4k
  return inner_concat(buffer, args...);
1916
31.4k
}
1917
1918
/**
1919
 * @private
1920
 * Concatenate the arguments and return a string.
1921
 * @returns a string
1922
 */
1923
template <typename... Args>
1924
506k
std::string concat(Args... args) {
1925
506k
  std::string answer;
1926
506k
  inner_concat(answer, args...);
1927
506k
  return answer;
1928
506k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1924
39.8k
std::string concat(Args... args) {
1925
39.8k
  std::string answer;
1926
39.8k
  inner_concat(answer, args...);
1927
39.8k
  return answer;
1928
39.8k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1924
20.5k
std::string concat(Args... args) {
1925
20.5k
  std::string answer;
1926
20.5k
  inner_concat(answer, args...);
1927
20.5k
  return answer;
1928
20.5k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1924
404k
std::string concat(Args... args) {
1925
404k
  std::string answer;
1926
404k
  inner_concat(answer, args...);
1927
404k
  return answer;
1928
404k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char const*)
Line
Count
Source
1924
10.5k
std::string concat(Args... args) {
1925
10.5k
  std::string answer;
1926
10.5k
  inner_concat(answer, args...);
1927
10.5k
  return answer;
1928
10.5k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1924
19
std::string concat(Args... args) {
1925
19
  std::string answer;
1926
19
  inner_concat(answer, args...);
1927
19
  return answer;
1928
19
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> > >(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
1924
31.4k
std::string concat(Args... args) {
1925
31.4k
  std::string answer;
1926
31.4k
  inner_concat(answer, args...);
1927
31.4k
  return answer;
1928
31.4k
}
1929
1930
/**
1931
 * @private
1932
 * @return Number of leading zeroes.
1933
 */
1934
1.14M
inline int leading_zeroes(uint32_t input_num) noexcept {
1935
#if ADA_REGULAR_VISUAL_STUDIO
1936
  unsigned long leading_zero(0);
1937
  unsigned long in(input_num);
1938
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
1939
#else
1940
1.14M
  return __builtin_clz(input_num);
1941
1.14M
#endif  // ADA_REGULAR_VISUAL_STUDIO
1942
1.14M
}
1943
1944
/**
1945
 * @private
1946
 * Counts the number of decimal digits necessary to represent x.
1947
 * faster than std::to_string(x).size().
1948
 * @return digit count
1949
 */
1950
34.0k
inline int fast_digit_count(uint32_t x) noexcept {
1951
34.0k
  auto int_log2 = [](uint32_t z) -> int {
1952
34.0k
    return 31 - ada::helpers::leading_zeroes(z | 1);
1953
34.0k
  };
1954
  // Compiles to very few instructions. Note that the
1955
  // table is static and thus effectively a constant.
1956
  // We leave it inside the function because it is meaningless
1957
  // outside of it (this comes at no performance cost).
1958
34.0k
  const static uint64_t table[] = {
1959
34.0k
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
1960
34.0k
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
1961
34.0k
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
1962
34.0k
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
1963
34.0k
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
1964
34.0k
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
1965
34.0k
      42949672960, 42949672960};
1966
34.0k
  return int((x + table[int_log2(x)]) >> 32);
1967
34.0k
}
1968
}  // namespace ada::helpers
1969
1970
#endif  // ADA_HELPERS_H
1971
/* end file include/ada/helpers.h */
1972
/* begin file include/ada/parser.h */
1973
/**
1974
 * @file parser.h
1975
 * @brief Low-level URL parsing functions.
1976
 *
1977
 * This header provides the internal URL parsing implementation. Most users
1978
 * should use `ada::parse()` from implementation.h instead of these functions
1979
 * directly.
1980
 *
1981
 * @see implementation.h for the recommended public API
1982
 */
1983
#ifndef ADA_PARSER_H
1984
#define ADA_PARSER_H
1985
1986
#include <string_view>
1987
#include <variant>
1988
1989
/* begin file include/ada/expected.h */
1990
/**
1991
 * @file expected.h
1992
 * @brief Definitions for std::expected
1993
 * @private Excluded from docs through the doxygen file.
1994
 */
1995
///
1996
// expected - An implementation of std::expected with extensions
1997
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
1998
//
1999
// Documentation available at http://tl.tartanllama.xyz/
2000
//
2001
// To the extent possible under law, the author(s) have dedicated all
2002
// copyright and related and neighboring rights to this software to the
2003
// public domain worldwide. This software is distributed without any warranty.
2004
//
2005
// You should have received a copy of the CC0 Public Domain Dedication
2006
// along with this software. If not, see
2007
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2008
///
2009
2010
#ifndef TL_EXPECTED_HPP
2011
#define TL_EXPECTED_HPP
2012
2013
#define TL_EXPECTED_VERSION_MAJOR 1
2014
#define TL_EXPECTED_VERSION_MINOR 1
2015
#define TL_EXPECTED_VERSION_PATCH 0
2016
2017
#include <exception>
2018
#include <functional>
2019
#include <type_traits>
2020
#include <utility>
2021
2022
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2023
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2024
#endif
2025
2026
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2027
#define TL_EXPECTED_MSVC2015
2028
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2029
#else
2030
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2031
#endif
2032
2033
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2034
     !defined(__clang__))
2035
#define TL_EXPECTED_GCC49
2036
#endif
2037
2038
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2039
     !defined(__clang__))
2040
#define TL_EXPECTED_GCC54
2041
#endif
2042
2043
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2044
     !defined(__clang__))
2045
#define TL_EXPECTED_GCC55
2046
#endif
2047
2048
#if !defined(TL_ASSERT)
2049
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2050
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2051
#include <cassert>
2052
20.2M
#define TL_ASSERT(x) assert(x)
2053
#else
2054
#define TL_ASSERT(x)
2055
#endif
2056
#endif
2057
2058
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2059
     !defined(__clang__))
2060
// GCC < 5 doesn't support overloading on const&& for member functions
2061
2062
#define TL_EXPECTED_NO_CONSTRR
2063
// GCC < 5 doesn't support some standard C++11 type traits
2064
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2065
  std::has_trivial_copy_constructor<T>
2066
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2067
  std::has_trivial_copy_assign<T>
2068
2069
// This one will be different for GCC 5.7 if it's ever supported
2070
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2071
  std::is_trivially_destructible<T>
2072
2073
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2074
// std::vector for non-copyable types
2075
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2076
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2077
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2078
namespace tl {
2079
namespace detail {
2080
template <class T>
2081
struct is_trivially_copy_constructible
2082
    : std::is_trivially_copy_constructible<T> {};
2083
#ifdef _GLIBCXX_VECTOR
2084
template <class T, class A>
2085
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2086
#endif
2087
}  // namespace detail
2088
}  // namespace tl
2089
#endif
2090
2091
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2092
  tl::detail::is_trivially_copy_constructible<T>
2093
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2094
  std::is_trivially_copy_assignable<T>
2095
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2096
  std::is_trivially_destructible<T>
2097
#else
2098
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2099
  std::is_trivially_copy_constructible<T>
2100
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2101
  std::is_trivially_copy_assignable<T>
2102
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2103
  std::is_trivially_destructible<T>
2104
#endif
2105
2106
#if __cplusplus > 201103L
2107
#define TL_EXPECTED_CXX14
2108
#endif
2109
2110
#ifdef TL_EXPECTED_GCC49
2111
#define TL_EXPECTED_GCC49_CONSTEXPR
2112
#else
2113
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2114
#endif
2115
2116
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2117
     defined(TL_EXPECTED_GCC49))
2118
#define TL_EXPECTED_11_CONSTEXPR
2119
#else
2120
#define TL_EXPECTED_11_CONSTEXPR constexpr
2121
#endif
2122
2123
namespace tl {
2124
template <class T, class E>
2125
class expected;
2126
2127
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2128
#define TL_MONOSTATE_INPLACE_MUTEX
2129
class monostate {};
2130
2131
struct in_place_t {
2132
  explicit in_place_t() = default;
2133
};
2134
static constexpr in_place_t in_place{};
2135
#endif
2136
2137
template <class E>
2138
class unexpected {
2139
 public:
2140
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2141
2142
  unexpected() = delete;
2143
1.44k
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2144
2145
1.35M
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2146
2147
  template <class... Args, typename std::enable_if<std::is_constructible<
2148
                               E, Args&&...>::value>::type* = nullptr>
2149
  constexpr explicit unexpected(Args&&... args)
2150
66.0k
      : m_val(std::forward<Args>(args)...) {}
2151
  template <
2152
      class U, class... Args,
2153
      typename std::enable_if<std::is_constructible<
2154
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2155
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2156
      : m_val(l, std::forward<Args>(args)...) {}
2157
2158
  constexpr const E& value() const& { return m_val; }
2159
779k
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2160
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2161
  constexpr const E&& value() const&& { return std::move(m_val); }
2162
2163
 private:
2164
  E m_val;
2165
};
2166
2167
#ifdef __cpp_deduction_guides
2168
template <class E>
2169
unexpected(E) -> unexpected<E>;
2170
#endif
2171
2172
template <class E>
2173
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2174
  return lhs.value() == rhs.value();
2175
}
2176
template <class E>
2177
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2178
  return lhs.value() != rhs.value();
2179
}
2180
template <class E>
2181
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2182
  return lhs.value() < rhs.value();
2183
}
2184
template <class E>
2185
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2186
  return lhs.value() <= rhs.value();
2187
}
2188
template <class E>
2189
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2190
  return lhs.value() > rhs.value();
2191
}
2192
template <class E>
2193
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2194
  return lhs.value() >= rhs.value();
2195
}
2196
2197
template <class E>
2198
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2199
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2200
}
2201
2202
struct unexpect_t {
2203
  unexpect_t() = default;
2204
};
2205
static constexpr unexpect_t unexpect{};
2206
2207
namespace detail {
2208
template <typename E>
2209
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2210
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2211
0
  throw std::forward<E>(e);
2212
#else
2213
  (void)e;
2214
#ifdef _MSC_VER
2215
  __assume(0);
2216
#else
2217
  __builtin_unreachable();
2218
#endif
2219
#endif
2220
0
}
2221
2222
#ifndef TL_TRAITS_MUTEX
2223
#define TL_TRAITS_MUTEX
2224
// C++14-style aliases for brevity
2225
template <class T>
2226
using remove_const_t = typename std::remove_const<T>::type;
2227
template <class T>
2228
using remove_reference_t = typename std::remove_reference<T>::type;
2229
template <class T>
2230
using decay_t = typename std::decay<T>::type;
2231
template <bool E, class T = void>
2232
using enable_if_t = typename std::enable_if<E, T>::type;
2233
template <bool B, class T, class F>
2234
using conditional_t = typename std::conditional<B, T, F>::type;
2235
2236
// std::conjunction from C++17
2237
template <class...>
2238
struct conjunction : std::true_type {};
2239
template <class B>
2240
struct conjunction<B> : B {};
2241
template <class B, class... Bs>
2242
struct conjunction<B, Bs...>
2243
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2244
2245
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2246
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2247
#endif
2248
2249
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2250
// which results in a hard-error when using it in a noexcept expression
2251
// in some cases. This is a check to workaround the common failing case.
2252
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2253
template <class T>
2254
struct is_pointer_to_non_const_member_func : std::false_type {};
2255
template <class T, class Ret, class... Args>
2256
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2257
    : std::true_type {};
2258
template <class T, class Ret, class... Args>
2259
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2260
    : std::true_type {};
2261
template <class T, class Ret, class... Args>
2262
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2263
    : std::true_type {};
2264
template <class T, class Ret, class... Args>
2265
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2266
    : std::true_type {};
2267
template <class T, class Ret, class... Args>
2268
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2269
    : std::true_type {};
2270
template <class T, class Ret, class... Args>
2271
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2272
    : std::true_type {};
2273
2274
template <class T>
2275
struct is_const_or_const_ref : std::false_type {};
2276
template <class T>
2277
struct is_const_or_const_ref<T const&> : std::true_type {};
2278
template <class T>
2279
struct is_const_or_const_ref<T const> : std::true_type {};
2280
#endif
2281
2282
// std::invoke from C++17
2283
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2284
template <
2285
    typename Fn, typename... Args,
2286
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2287
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2288
                             is_const_or_const_ref<Args...>::value)>,
2289
#endif
2290
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2291
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2292
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2293
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2294
  return std::mem_fn(f)(std::forward<Args>(args)...);
2295
}
2296
2297
template <typename Fn, typename... Args,
2298
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2299
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2300
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2301
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2302
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2303
}
2304
2305
// std::invoke_result from C++17
2306
template <class F, class, class... Us>
2307
struct invoke_result_impl;
2308
2309
template <class F, class... Us>
2310
struct invoke_result_impl<
2311
    F,
2312
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2313
    Us...> {
2314
  using type =
2315
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2316
};
2317
2318
template <class F, class... Us>
2319
using invoke_result = invoke_result_impl<F, void, Us...>;
2320
2321
template <class F, class... Us>
2322
using invoke_result_t = typename invoke_result<F, Us...>::type;
2323
2324
#if defined(_MSC_VER) && _MSC_VER <= 1900
2325
// TODO make a version which works with MSVC 2015
2326
template <class T, class U = T>
2327
struct is_swappable : std::true_type {};
2328
2329
template <class T, class U = T>
2330
struct is_nothrow_swappable : std::true_type {};
2331
#else
2332
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2333
namespace swap_adl_tests {
2334
// if swap ADL finds this then it would call std::swap otherwise (same
2335
// signature)
2336
struct tag {};
2337
2338
template <class T>
2339
tag swap(T&, T&);
2340
template <class T, std::size_t N>
2341
tag swap(T (&a)[N], T (&b)[N]);
2342
2343
// helper functions to test if an unqualified swap is possible, and if it
2344
// becomes std::swap
2345
template <class, class>
2346
std::false_type can_swap(...) noexcept(false);
2347
template <class T, class U,
2348
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2349
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2350
                                                    std::declval<U&>())));
2351
2352
template <class, class>
2353
std::false_type uses_std(...);
2354
template <class T, class U>
2355
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2356
uses_std(int);
2357
2358
template <class T>
2359
struct is_std_swap_noexcept
2360
    : std::integral_constant<bool,
2361
                             std::is_nothrow_move_constructible<T>::value &&
2362
                                 std::is_nothrow_move_assignable<T>::value> {};
2363
2364
template <class T, std::size_t N>
2365
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2366
2367
template <class T, class U>
2368
struct is_adl_swap_noexcept
2369
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2370
}  // namespace swap_adl_tests
2371
2372
template <class T, class U = T>
2373
struct is_swappable
2374
    : std::integral_constant<
2375
          bool,
2376
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2377
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2378
               (std::is_move_assignable<T>::value &&
2379
                std::is_move_constructible<T>::value))> {};
2380
2381
template <class T, std::size_t N>
2382
struct is_swappable<T[N], T[N]>
2383
    : std::integral_constant<
2384
          bool,
2385
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2386
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2387
                   0))::value ||
2388
               is_swappable<T, T>::value)> {};
2389
2390
template <class T, class U = T>
2391
struct is_nothrow_swappable
2392
    : std::integral_constant<
2393
          bool,
2394
          is_swappable<T, U>::value &&
2395
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2396
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2397
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2398
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2399
#endif
2400
#endif
2401
2402
// Trait for checking if a type is a tl::expected
2403
template <class T>
2404
struct is_expected_impl : std::false_type {};
2405
template <class T, class E>
2406
struct is_expected_impl<expected<T, E>> : std::true_type {};
2407
template <class T>
2408
using is_expected = is_expected_impl<decay_t<T>>;
2409
2410
template <class T, class E, class U>
2411
using expected_enable_forward_value = detail::enable_if_t<
2412
    std::is_constructible<T, U&&>::value &&
2413
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2414
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2415
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2416
2417
template <class T, class E, class U, class G, class UR, class GR>
2418
using expected_enable_from_other = detail::enable_if_t<
2419
    std::is_constructible<T, UR>::value &&
2420
    std::is_constructible<E, GR>::value &&
2421
    !std::is_constructible<T, expected<U, G>&>::value &&
2422
    !std::is_constructible<T, expected<U, G>&&>::value &&
2423
    !std::is_constructible<T, const expected<U, G>&>::value &&
2424
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2425
    !std::is_convertible<expected<U, G>&, T>::value &&
2426
    !std::is_convertible<expected<U, G>&&, T>::value &&
2427
    !std::is_convertible<const expected<U, G>&, T>::value &&
2428
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2429
2430
template <class T, class U>
2431
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2432
2433
template <class T>
2434
using is_copy_constructible_or_void =
2435
    is_void_or<T, std::is_copy_constructible<T>>;
2436
2437
template <class T>
2438
using is_move_constructible_or_void =
2439
    is_void_or<T, std::is_move_constructible<T>>;
2440
2441
template <class T>
2442
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2443
2444
template <class T>
2445
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2446
2447
}  // namespace detail
2448
2449
namespace detail {
2450
struct no_init_t {};
2451
static constexpr no_init_t no_init{};
2452
2453
// Implements the storage of the values, and ensures that the destructor is
2454
// trivial if it can be.
2455
//
2456
// This specialization is for where neither `T` or `E` is trivially
2457
// destructible, so the destructors must be called on destruction of the
2458
// `expected`
2459
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2460
          bool = std::is_trivially_destructible<E>::value>
2461
struct expected_storage_base {
2462
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2463
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2464
2465
  template <class... Args,
2466
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2467
                nullptr>
2468
  constexpr expected_storage_base(in_place_t, Args&&... args)
2469
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2470
2471
  template <class U, class... Args,
2472
            detail::enable_if_t<std::is_constructible<
2473
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2474
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2475
                                  Args&&... args)
2476
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2477
  template <class... Args,
2478
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2479
                nullptr>
2480
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2481
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2482
2483
  template <class U, class... Args,
2484
            detail::enable_if_t<std::is_constructible<
2485
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2486
  constexpr explicit expected_storage_base(unexpect_t,
2487
                                           std::initializer_list<U> il,
2488
                                           Args&&... args)
2489
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2490
2491
  ~expected_storage_base() {
2492
    if (m_has_val) {
2493
      m_val.~T();
2494
    } else {
2495
      m_unexpect.~unexpected<E>();
2496
    }
2497
  }
2498
  union {
2499
    T m_val;
2500
    unexpected<E> m_unexpect;
2501
    char m_no_init;
2502
  };
2503
  bool m_has_val;
2504
};
2505
2506
// This specialization is for when both `T` and `E` are trivially-destructible,
2507
// so the destructor of the `expected` can be trivial.
2508
template <class T, class E>
2509
struct expected_storage_base<T, E, true, true> {
2510
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2511
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2512
2513
  template <class... Args,
2514
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2515
                nullptr>
2516
  constexpr expected_storage_base(in_place_t, Args&&... args)
2517
324k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS4_11char_traitsIcEEEELNS2_27url_search_params_iter_typeE0EEENS2_6errorsELb1ELb1EEC2IJSA_ETnPNS4_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS4_11char_traitsIcEEEELNS2_27url_search_params_iter_typeE1EEENS2_6errorsELb1ELb1EEC2IJSA_ETnPNS4_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada22url_search_params_iterINSt3__14pairINS4_17basic_string_viewIcNS4_11char_traitsIcEEEES9_EELNS2_27url_search_params_iter_typeE2EEENS2_6errorsELb1ELb1EEC2IJSC_ETnPNS4_9enable_ifIXsr3std16is_constructibleISC_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
2518
2519
  template <class U, class... Args,
2520
            detail::enable_if_t<std::is_constructible<
2521
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2522
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2523
                                  Args&&... args)
2524
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2525
  template <class... Args,
2526
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2527
                nullptr>
2528
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2529
0
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2530
2531
  template <class U, class... Args,
2532
            detail::enable_if_t<std::is_constructible<
2533
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2534
  constexpr explicit expected_storage_base(unexpect_t,
2535
                                           std::initializer_list<U> il,
2536
                                           Args&&... args)
2537
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2538
2539
  ~expected_storage_base() = default;
2540
  union {
2541
    T m_val;
2542
    unexpected<E> m_unexpect;
2543
    char m_no_init;
2544
  };
2545
  bool m_has_val;
2546
};
2547
2548
// T is trivial, E is not.
2549
template <class T, class E>
2550
struct expected_storage_base<T, E, true, false> {
2551
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2552
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2553
      : m_no_init(), m_has_val(false) {}
2554
2555
  template <class... Args,
2556
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2557
                nullptr>
2558
  constexpr expected_storage_base(in_place_t, Args&&... args)
2559
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2560
2561
  template <class U, class... Args,
2562
            detail::enable_if_t<std::is_constructible<
2563
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2564
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2565
                                  Args&&... args)
2566
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2567
  template <class... Args,
2568
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2569
                nullptr>
2570
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2571
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2572
2573
  template <class U, class... Args,
2574
            detail::enable_if_t<std::is_constructible<
2575
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2576
  constexpr explicit expected_storage_base(unexpect_t,
2577
                                           std::initializer_list<U> il,
2578
                                           Args&&... args)
2579
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2580
2581
  ~expected_storage_base() {
2582
    if (!m_has_val) {
2583
      m_unexpect.~unexpected<E>();
2584
    }
2585
  }
2586
2587
  union {
2588
    T m_val;
2589
    unexpected<E> m_unexpect;
2590
    char m_no_init;
2591
  };
2592
  bool m_has_val;
2593
};
2594
2595
// E is trivial, T is not.
2596
template <class T, class E>
2597
struct expected_storage_base<T, E, false, true> {
2598
540k
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2599
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2600
2601
  template <class... Args,
2602
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2603
                nullptr>
2604
  constexpr expected_storage_base(in_place_t, Args&&... args)
2605
6.83M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2605
248k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2605
1.01M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2605
328k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2605
855k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA1_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Line
Count
Source
2605
947k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA2_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Line
Count
Source
2605
58
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJRS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Line
Count
Source
2605
996k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada17url_search_paramsENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEEN3ada6errorsELb0ELb1EEC2IJSB_ETnPNS2_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
_ZN2tl6detail21expected_storage_baseINSt3__18optionalIN3ada18url_pattern_resultEEENS4_6errorsELb0ELb1EEC2IJRKNS2_9nullopt_tEETnPNS2_9enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2605
385k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__18optionalIN3ada18url_pattern_resultEEENS4_6errorsELb0ELb1EEC2IJS5_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
2605
46.6k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJRS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
Line
Count
Source
2605
73.3k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJRS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2605
917k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada21url_pattern_componentINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS6_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESE_
Line
Count
Source
2605
916k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada11url_patternINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS6_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESE_
Line
Count
Source
2605
108k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2606
2607
  template <class U, class... Args,
2608
            detail::enable_if_t<std::is_constructible<
2609
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2610
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2611
                                  Args&&... args)
2612
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2613
  template <class... Args,
2614
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2615
                nullptr>
2616
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2617
713k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2617
25.3k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2617
530k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2617
49.7k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Line
Count
Source
2617
3.80k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Line
Count
Source
2617
1.19k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__18optionalIN3ada18url_pattern_resultEEENS4_6errorsELb0ELb1EEC2IJS7_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESD_
_ZN2tl6detail21expected_storage_baseIN3ada11url_patternINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS7_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESE_
Line
Count
Source
2617
91.4k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESF_
Line
Count
Source
2617
5.36k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada21url_pattern_componentINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS7_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESE_
Line
Count
Source
2617
6.55k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2618
2619
  template <class U, class... Args,
2620
            detail::enable_if_t<std::is_constructible<
2621
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2622
  constexpr explicit expected_storage_base(unexpect_t,
2623
                                           std::initializer_list<U> il,
2624
                                           Args&&... args)
2625
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2626
2627
8.09M
  ~expected_storage_base() {
2628
8.09M
    if (m_has_val) {
2629
7.37M
      m_val.~T();
2630
7.37M
    }
2631
8.09M
  }
tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
273k
  ~expected_storage_base() {
2628
273k
    if (m_has_val) {
2629
248k
      m_val.~T();
2630
248k
    }
2631
273k
  }
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
2.08M
  ~expected_storage_base() {
2628
2.08M
    if (m_has_val) {
2629
1.55M
      m_val.~T();
2630
1.55M
    }
2631
2.08M
  }
tl::detail::expected_storage_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
1.80M
  ~expected_storage_base() {
2628
1.80M
    if (m_has_val) {
2629
1.80M
      m_val.~T();
2630
1.80M
    }
2631
1.80M
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url_search_params, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, false, true>::~expected_storage_base()
tl::detail::expected_storage_base<ada::url_pattern_init, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
452k
  ~expected_storage_base() {
2628
452k
    if (m_has_val) {
2629
402k
      m_val.~T();
2630
402k
    }
2631
452k
  }
tl::detail::expected_storage_base<std::__1::optional<ada::url_pattern_result>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
432k
  ~expected_storage_base() {
2628
432k
    if (m_has_val) {
2629
432k
      m_val.~T();
2630
432k
    }
2631
432k
  }
tl::detail::expected_storage_base<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
199k
  ~expected_storage_base() {
2628
199k
    if (m_has_val) {
2629
108k
      m_val.~T();
2630
108k
    }
2631
199k
  }
tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
997k
  ~expected_storage_base() {
2628
997k
    if (m_has_val) {
2629
996k
      m_val.~T();
2630
996k
    }
2631
997k
  }
tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
922k
  ~expected_storage_base() {
2628
922k
    if (m_has_val) {
2629
917k
      m_val.~T();
2630
917k
    }
2631
922k
  }
tl::detail::expected_storage_base<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2627
922k
  ~expected_storage_base() {
2628
922k
    if (m_has_val) {
2629
916k
      m_val.~T();
2630
916k
    }
2631
922k
  }
2632
  union {
2633
    T m_val;
2634
    unexpected<E> m_unexpect;
2635
    char m_no_init;
2636
  };
2637
  bool m_has_val;
2638
};
2639
2640
// `T` is `void`, `E` is trivially-destructible
2641
template <class E>
2642
struct expected_storage_base<void, E, false, true> {
2643
#if __GNUC__ <= 5
2644
// no constexpr for GCC 4/5 bug
2645
#else
2646
  TL_EXPECTED_MSVC2015_CONSTEXPR
2647
#endif
2648
  expected_storage_base() : m_has_val(true) {}
2649
2650
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2651
2652
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2653
2654
  template <class... Args,
2655
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2656
                nullptr>
2657
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2658
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2659
2660
  template <class U, class... Args,
2661
            detail::enable_if_t<std::is_constructible<
2662
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2663
  constexpr explicit expected_storage_base(unexpect_t,
2664
                                           std::initializer_list<U> il,
2665
                                           Args&&... args)
2666
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2667
2668
  ~expected_storage_base() = default;
2669
  struct dummy {};
2670
  union {
2671
    unexpected<E> m_unexpect;
2672
    dummy m_val;
2673
  };
2674
  bool m_has_val;
2675
};
2676
2677
// `T` is `void`, `E` is not trivially-destructible
2678
template <class E>
2679
struct expected_storage_base<void, E, false, false> {
2680
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2681
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2682
2683
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2684
2685
  template <class... Args,
2686
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2687
                nullptr>
2688
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2689
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2690
2691
  template <class U, class... Args,
2692
            detail::enable_if_t<std::is_constructible<
2693
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2694
  constexpr explicit expected_storage_base(unexpect_t,
2695
                                           std::initializer_list<U> il,
2696
                                           Args&&... args)
2697
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2698
2699
  ~expected_storage_base() {
2700
    if (!m_has_val) {
2701
      m_unexpect.~unexpected<E>();
2702
    }
2703
  }
2704
2705
  union {
2706
    unexpected<E> m_unexpect;
2707
    char m_dummy;
2708
  };
2709
  bool m_has_val;
2710
};
2711
2712
// This base class provides some handy member functions which can be used in
2713
// further derived classes
2714
template <class T, class E>
2715
struct expected_operations_base : expected_storage_base<T, E> {
2716
  using expected_storage_base<T, E>::expected_storage_base;
2717
2718
  template <class... Args>
2719
0
  void construct(Args&&... args) noexcept {
2720
0
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2721
0
    this->m_has_val = true;
2722
0
  }
2723
2724
  template <class Rhs>
2725
  // NOLINTNEXTLINE(bugprone-exception-escape)
2726
0
  void construct_with(Rhs&& rhs) noexcept {
2727
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2728
0
    this->m_has_val = true;
2729
0
  }
2730
2731
  template <class... Args>
2732
0
  void construct_error(Args&&... args) noexcept {
2733
0
    new (std::addressof(this->m_unexpect))
2734
0
        unexpected<E>(std::forward<Args>(args)...);
2735
0
    this->m_has_val = false;
2736
0
  }
Unexecuted instantiation: void tl::detail::expected_operations_base<ada::url_aggregator, ada::errors>::construct_error<tl::unexpected<ada::errors> const&>(tl::unexpected<ada::errors> const&)
Unexecuted instantiation: void tl::detail::expected_operations_base<ada::url_aggregator, ada::errors>::construct_error<tl::unexpected<ada::errors> >(tl::unexpected<ada::errors>&&)
2737
2738
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2739
2740
  // These assign overloads ensure that the most efficient assignment
2741
  // implementation is used while maintaining the strong exception guarantee.
2742
  // The problematic case is where rhs has a value, but *this does not.
2743
  //
2744
  // This overload handles the case where we can just copy-construct `T`
2745
  // directly into place without throwing.
2746
  template <class U = T,
2747
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2748
                nullptr>
2749
  void assign(const expected_operations_base& rhs) noexcept {
2750
    if (!this->m_has_val && rhs.m_has_val) {
2751
      geterr().~unexpected<E>();
2752
      construct(rhs.get());
2753
    } else {
2754
      assign_common(rhs);
2755
    }
2756
  }
2757
2758
  // This overload handles the case where we can attempt to create a copy of
2759
  // `T`, then no-throw move it into place if the copy was successful.
2760
  template <class U = T,
2761
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2762
                                std::is_nothrow_move_constructible<U>::value>* =
2763
                nullptr>
2764
  void assign(const expected_operations_base& rhs) noexcept {
2765
    if (!this->m_has_val && rhs.m_has_val) {
2766
      T tmp = rhs.get();
2767
      geterr().~unexpected<E>();
2768
      construct(std::move(tmp));
2769
    } else {
2770
      assign_common(rhs);
2771
    }
2772
  }
2773
2774
  // This overload is the worst-case, where we have to move-construct the
2775
  // unexpected value into temporary storage, then try to copy the T into place.
2776
  // If the construction succeeds, then everything is fine, but if it throws,
2777
  // then we move the old unexpected value back into place before rethrowing the
2778
  // exception.
2779
  template <class U = T,
2780
            detail::enable_if_t<
2781
                !std::is_nothrow_copy_constructible<U>::value &&
2782
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2783
  void assign(const expected_operations_base& rhs) {
2784
    if (!this->m_has_val && rhs.m_has_val) {
2785
      auto tmp = std::move(geterr());
2786
      geterr().~unexpected<E>();
2787
2788
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2789
      try {
2790
        construct(rhs.get());
2791
      } catch (...) {
2792
        geterr() = std::move(tmp);
2793
        throw;
2794
      }
2795
#else
2796
      construct(rhs.get());
2797
#endif
2798
    } else {
2799
      assign_common(rhs);
2800
    }
2801
  }
2802
2803
  // These overloads do the same as above, but for rvalues
2804
  template <class U = T,
2805
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2806
                nullptr>
2807
216k
  void assign(expected_operations_base&& rhs) noexcept {
2808
216k
    if (!this->m_has_val && rhs.m_has_val) {
2809
0
      geterr().~unexpected<E>();
2810
0
      construct(std::move(rhs).get());
2811
216k
    } else {
2812
216k
      assign_common(std::move(rhs));
2813
216k
    }
2814
216k
  }
2815
2816
  template <class U = T,
2817
            detail::enable_if_t<
2818
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2819
  void assign(expected_operations_base&& rhs) {
2820
    if (!this->m_has_val && rhs.m_has_val) {
2821
      auto tmp = std::move(geterr());
2822
      geterr().~unexpected<E>();
2823
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2824
      try {
2825
        construct(std::move(rhs).get());
2826
      } catch (...) {
2827
        geterr() = std::move(tmp);
2828
        throw;
2829
      }
2830
#else
2831
      construct(std::move(rhs).get());
2832
#endif
2833
    } else {
2834
      assign_common(std::move(rhs));
2835
    }
2836
  }
2837
2838
#else
2839
2840
  // If exceptions are disabled then we can just copy-construct
2841
  void assign(const expected_operations_base& rhs) noexcept {
2842
    if (!this->m_has_val && rhs.m_has_val) {
2843
      geterr().~unexpected<E>();
2844
      construct(rhs.get());
2845
    } else {
2846
      assign_common(rhs);
2847
    }
2848
  }
2849
2850
  void assign(expected_operations_base&& rhs) noexcept {
2851
    if (!this->m_has_val && rhs.m_has_val) {
2852
      geterr().~unexpected<E>();
2853
      construct(std::move(rhs).get());
2854
    } else {
2855
      assign_common(std::move(rhs));
2856
    }
2857
  }
2858
2859
#endif
2860
2861
  // The common part of move/copy assigning
2862
  template <class Rhs>
2863
216k
  void assign_common(Rhs&& rhs) {
2864
216k
    if (this->m_has_val) {
2865
216k
      if (rhs.m_has_val) {
2866
216k
        get() = std::forward<Rhs>(rhs).get();
2867
216k
      } else {
2868
0
        destroy_val();
2869
0
        construct_error(std::forward<Rhs>(rhs).geterr());
2870
0
      }
2871
216k
    } else {
2872
0
      if (!rhs.m_has_val) {
2873
0
        geterr() = std::forward<Rhs>(rhs).geterr();
2874
0
      }
2875
0
    }
2876
216k
  }
2877
2878
0
  bool has_value() const { return this->m_has_val; }
2879
2880
216k
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2881
0
  constexpr const T& get() const& { return this->m_val; }
2882
216k
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2883
#ifndef TL_EXPECTED_NO_CONSTRR
2884
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2885
#endif
2886
2887
0
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2888
0
    return this->m_unexpect;
2889
0
  }
2890
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2891
0
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2892
0
    return std::move(this->m_unexpect);
2893
0
  }
2894
#ifndef TL_EXPECTED_NO_CONSTRR
2895
  constexpr const unexpected<E>&& geterr() const&& {
2896
    return std::move(this->m_unexpect);
2897
  }
2898
#endif
2899
2900
0
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2901
};
2902
2903
// This base class provides some handy member functions which can be used in
2904
// further derived classes
2905
template <class E>
2906
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2907
  using expected_storage_base<void, E>::expected_storage_base;
2908
2909
  template <class... Args>
2910
  void construct() noexcept {
2911
    this->m_has_val = true;
2912
  }
2913
2914
  // This function doesn't use its argument, but needs it so that code in
2915
  // levels above this can work independently of whether T is void
2916
  template <class Rhs>
2917
  void construct_with(Rhs&&) noexcept {
2918
    this->m_has_val = true;
2919
  }
2920
2921
  template <class... Args>
2922
  void construct_error(Args&&... args) noexcept {
2923
    new (std::addressof(this->m_unexpect))
2924
        unexpected<E>(std::forward<Args>(args)...);
2925
    this->m_has_val = false;
2926
  }
2927
2928
  template <class Rhs>
2929
  void assign(Rhs&& rhs) noexcept {
2930
    if (!this->m_has_val) {
2931
      if (rhs.m_has_val) {
2932
        geterr().~unexpected<E>();
2933
        construct();
2934
      } else {
2935
        geterr() = std::forward<Rhs>(rhs).geterr();
2936
      }
2937
    } else {
2938
      if (!rhs.m_has_val) {
2939
        construct_error(std::forward<Rhs>(rhs).geterr());
2940
      }
2941
    }
2942
  }
2943
2944
  bool has_value() const { return this->m_has_val; }
2945
2946
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2947
    return this->m_unexpect;
2948
  }
2949
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2950
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2951
    return std::move(this->m_unexpect);
2952
  }
2953
#ifndef TL_EXPECTED_NO_CONSTRR
2954
  constexpr const unexpected<E>&& geterr() const&& {
2955
    return std::move(this->m_unexpect);
2956
  }
2957
#endif
2958
2959
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
2960
    // no-op
2961
  }
2962
};
2963
2964
// This class manages conditionally having a trivial copy constructor
2965
// This specialization is for when T and E are trivially copy constructible
2966
template <class T, class E,
2967
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
2968
                                   T)>::value &&
2969
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
2970
struct expected_copy_base : expected_operations_base<T, E> {
2971
  using expected_operations_base<T, E>::expected_operations_base;
2972
};
2973
2974
// This specialization is for when T or E are not trivially copy constructible
2975
template <class T, class E>
2976
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
2977
  using expected_operations_base<T, E>::expected_operations_base;
2978
2979
540k
  expected_copy_base() = default;
2980
  expected_copy_base(const expected_copy_base& rhs)
2981
0
      : expected_operations_base<T, E>(no_init) {
2982
0
    if (rhs.has_value()) {
2983
0
      this->construct_with(rhs);
2984
0
    } else {
2985
0
      this->construct_error(rhs.geterr());
2986
0
    }
2987
0
  }
2988
2989
  expected_copy_base(expected_copy_base&& rhs) = default;
2990
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
2991
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
2992
};
2993
2994
// This class manages conditionally having a trivial move constructor
2995
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
2996
// doesn't implement an analogue to std::is_trivially_move_constructible. We
2997
// have to make do with a non-trivial move constructor even if T is trivially
2998
// move constructible
2999
#ifndef TL_EXPECTED_GCC49
3000
template <class T, class E,
3001
          bool =
3002
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3003
              std::is_trivially_move_constructible<E>::value>
3004
struct expected_move_base : expected_copy_base<T, E> {
3005
  using expected_copy_base<T, E>::expected_copy_base;
3006
};
3007
#else
3008
template <class T, class E, bool = false>
3009
struct expected_move_base;
3010
#endif
3011
template <class T, class E>
3012
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3013
  using expected_copy_base<T, E>::expected_copy_base;
3014
3015
540k
  expected_move_base() = default;
3016
0
  expected_move_base(const expected_move_base& rhs) = default;
3017
3018
  expected_move_base(expected_move_base&& rhs) noexcept(
3019
      std::is_nothrow_move_constructible<T>::value)
3020
      : expected_copy_base<T, E>(no_init) {
3021
    if (rhs.has_value()) {
3022
      this->construct_with(std::move(rhs));
3023
    } else {
3024
      this->construct_error(std::move(rhs.geterr()));
3025
    }
3026
  }
3027
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3028
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3029
};
3030
3031
// This class manages conditionally having a trivial copy assignment operator
3032
template <
3033
    class T, class E,
3034
    bool =
3035
        is_void_or<
3036
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3037
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3038
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3039
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3040
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3041
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3042
struct expected_copy_assign_base : expected_move_base<T, E> {
3043
  using expected_move_base<T, E>::expected_move_base;
3044
};
3045
3046
template <class T, class E>
3047
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3048
  using expected_move_base<T, E>::expected_move_base;
3049
3050
540k
  expected_copy_assign_base() = default;
3051
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3052
3053
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3054
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3055
    this->assign(rhs);
3056
    return *this;
3057
  }
3058
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3059
      default;
3060
};
3061
3062
// This class manages conditionally having a trivial move assignment operator
3063
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3064
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3065
// to make do with a non-trivial move assignment operator even if T is trivially
3066
// move assignable
3067
#ifndef TL_EXPECTED_GCC49
3068
template <
3069
    class T, class E,
3070
    bool = is_void_or<
3071
               T, conjunction<std::is_trivially_destructible<T>,
3072
                              std::is_trivially_move_constructible<T>,
3073
                              std::is_trivially_move_assignable<T>>>::value &&
3074
           std::is_trivially_destructible<E>::value &&
3075
           std::is_trivially_move_constructible<E>::value &&
3076
           std::is_trivially_move_assignable<E>::value>
3077
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3078
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3079
};
3080
#else
3081
template <class T, class E, bool = false>
3082
struct expected_move_assign_base;
3083
#endif
3084
3085
template <class T, class E>
3086
struct expected_move_assign_base<T, E, false>
3087
    : expected_copy_assign_base<T, E> {
3088
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3089
3090
540k
  expected_move_assign_base() = default;
3091
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3092
3093
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3094
3095
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3096
      default;
3097
3098
  expected_move_assign_base&
3099
  operator=(expected_move_assign_base&& rhs) noexcept(
3100
      std::is_nothrow_move_constructible<T>::value &&
3101
216k
      std::is_nothrow_move_assignable<T>::value) {
3102
216k
    this->assign(std::move(rhs));
3103
216k
    return *this;
3104
216k
  }
3105
};
3106
3107
// expected_delete_ctor_base will conditionally delete copy and move
3108
// constructors depending on whether T is copy/move constructible
3109
template <class T, class E,
3110
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3111
                             std::is_copy_constructible<E>::value),
3112
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3113
                             std::is_move_constructible<E>::value)>
3114
struct expected_delete_ctor_base {
3115
  expected_delete_ctor_base() = default;
3116
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3117
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3118
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3119
      default;
3120
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3121
      default;
3122
};
3123
3124
template <class T, class E>
3125
struct expected_delete_ctor_base<T, E, true, false> {
3126
  expected_delete_ctor_base() = default;
3127
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3128
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3129
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3130
      default;
3131
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3132
      default;
3133
};
3134
3135
template <class T, class E>
3136
struct expected_delete_ctor_base<T, E, false, true> {
3137
  expected_delete_ctor_base() = default;
3138
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3139
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3140
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3141
      default;
3142
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3143
      default;
3144
};
3145
3146
template <class T, class E>
3147
struct expected_delete_ctor_base<T, E, false, false> {
3148
  expected_delete_ctor_base() = default;
3149
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3150
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3151
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3152
      default;
3153
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3154
      default;
3155
};
3156
3157
// expected_delete_assign_base will conditionally delete copy and move
3158
// constructors depending on whether T and E are copy/move constructible +
3159
// assignable
3160
template <class T, class E,
3161
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3162
                             std::is_copy_constructible<E>::value &&
3163
                             is_copy_assignable_or_void<T>::value &&
3164
                             std::is_copy_assignable<E>::value),
3165
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3166
                             std::is_move_constructible<E>::value &&
3167
                             is_move_assignable_or_void<T>::value &&
3168
                             std::is_move_assignable<E>::value)>
3169
struct expected_delete_assign_base {
3170
  expected_delete_assign_base() = default;
3171
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3172
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3173
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3174
      default;
3175
  expected_delete_assign_base& operator=(
3176
      expected_delete_assign_base&&) noexcept = default;
3177
};
3178
3179
template <class T, class E>
3180
struct expected_delete_assign_base<T, E, true, false> {
3181
  expected_delete_assign_base() = default;
3182
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3183
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3184
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3185
      default;
3186
  expected_delete_assign_base& operator=(
3187
      expected_delete_assign_base&&) noexcept = delete;
3188
};
3189
3190
template <class T, class E>
3191
struct expected_delete_assign_base<T, E, false, true> {
3192
  expected_delete_assign_base() = default;
3193
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3194
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3195
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3196
      delete;
3197
  expected_delete_assign_base& operator=(
3198
      expected_delete_assign_base&&) noexcept = default;
3199
};
3200
3201
template <class T, class E>
3202
struct expected_delete_assign_base<T, E, false, false> {
3203
  expected_delete_assign_base() = default;
3204
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3205
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3206
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3207
      delete;
3208
  expected_delete_assign_base& operator=(
3209
      expected_delete_assign_base&&) noexcept = delete;
3210
};
3211
3212
// This is needed to be able to construct the expected_default_ctor_base which
3213
// follows, while still conditionally deleting the default constructor.
3214
struct default_constructor_tag {
3215
  explicit constexpr default_constructor_tag() = default;
3216
};
3217
3218
// expected_default_ctor_base will ensure that expected has a deleted default
3219
// consturctor if T is not default constructible.
3220
// This specialization is for when T is default constructible
3221
template <class T, class E,
3222
          bool Enable =
3223
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3224
struct expected_default_ctor_base {
3225
  constexpr expected_default_ctor_base() noexcept = default;
3226
  constexpr expected_default_ctor_base(
3227
      expected_default_ctor_base const&) noexcept = default;
3228
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3229
      default;
3230
  expected_default_ctor_base& operator=(
3231
      expected_default_ctor_base const&) noexcept = default;
3232
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3233
      default;
3234
3235
7.87M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
273k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
1.54M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern_init, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
452k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
1.80M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
997k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
tl::detail::expected_default_ctor_base<bool, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
324k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::optional<ada::url_pattern_result>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
432k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
199k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
922k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3235
922k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3236
};
3237
3238
// This specialization is for when T is not default constructible
3239
template <class T, class E>
3240
struct expected_default_ctor_base<T, E, false> {
3241
  constexpr expected_default_ctor_base() noexcept = delete;
3242
  constexpr expected_default_ctor_base(
3243
      expected_default_ctor_base const&) noexcept = default;
3244
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3245
      default;
3246
  expected_default_ctor_base& operator=(
3247
      expected_default_ctor_base const&) noexcept = default;
3248
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3249
      default;
3250
3251
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3252
};
3253
}  // namespace detail
3254
3255
template <class E>
3256
class bad_expected_access : public std::exception {
3257
 public:
3258
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3259
3260
0
  virtual const char* what() const noexcept override {
3261
0
    return "Bad expected access";
3262
0
  }
3263
3264
  const E& error() const& { return m_val; }
3265
  E& error() & { return m_val; }
3266
  const E&& error() const&& { return std::move(m_val); }
3267
  E&& error() && { return std::move(m_val); }
3268
3269
 private:
3270
  E m_val;
3271
};
3272
3273
/// An `expected<T, E>` object is an object that contains the storage for
3274
/// another object and manages the lifetime of this contained object `T`.
3275
/// Alternatively it could contain the storage for another unexpected object
3276
/// `E`. The contained object may not be initialized after the expected object
3277
/// has been initialized, and may not be destroyed before the expected object
3278
/// has been destroyed. The initialization state of the contained object is
3279
/// tracked by the expected object.
3280
template <class T, class E>
3281
class expected : private detail::expected_move_assign_base<T, E>,
3282
                 private detail::expected_delete_ctor_base<T, E>,
3283
                 private detail::expected_delete_assign_base<T, E>,
3284
                 private detail::expected_default_ctor_base<T, E> {
3285
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3286
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3287
                "T must not be in_place_t");
3288
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3289
                "T must not be unexpect_t");
3290
  static_assert(
3291
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3292
      "T must not be unexpected<E>");
3293
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3294
3295
11.4M
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url, ada::errors>::valptr()
Line
Count
Source
3295
905k
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3295
3.38M
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::valptr()
tl::expected<ada::url_pattern_init, ada::errors>::valptr()
Line
Count
Source
3295
4.42M
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::valptr()
Line
Count
Source
3295
648k
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::valptr()
Line
Count
Source
3295
2.06M
  T* valptr() { return std::addressof(this->m_val); }
3296
  const T* valptr() const { return std::addressof(this->m_val); }
3297
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3298
  const unexpected<E>* errptr() const {
3299
    return std::addressof(this->m_unexpect);
3300
  }
3301
3302
  template <class U = T,
3303
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3304
8.72M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
8.72M
    return this->m_val;
3306
8.72M
  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3304
356k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
356k
    return this->m_val;
3306
356k
  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3304
982k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
982k
    return this->m_val;
3306
982k
  }
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3304
1.80M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
1.80M
    return this->m_val;
3306
1.80M
  }
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3304
216k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
216k
    return this->m_val;
3306
216k
  }
_ZN2tl8expectedIbN3ada6errorsEE3valIbTnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS7_v
Line
Count
Source
3304
324k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
324k
    return this->m_val;
3306
324k
  }
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEE3valIS5_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSA_v
Line
Count
Source
3304
38.8k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
38.8k
    return this->m_val;
3306
38.8k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3304
996k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
996k
    return this->m_val;
3306
996k
  }
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3304
73.3k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
73.3k
    return this->m_val;
3306
73.3k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
3304
3.01M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
3.01M
    return this->m_val;
3306
3.01M
  }
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3304
916k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3305
916k
    return this->m_val;
3306
916k
  }
3307
66.2k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::err()
Line
Count
Source
3307
3.80k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::err()
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::err()
Line
Count
Source
3307
1.19k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_init, ada::errors>::err()
Line
Count
Source
3307
49.3k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::err()
Line
Count
Source
3307
5.36k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::err()
Line
Count
Source
3307
6.55k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
3308
3309
  template <class U = T,
3310
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3311
  constexpr const U& val() const {
3312
    return this->m_val;
3313
  }
3314
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3315
3316
  using impl_base = detail::expected_move_assign_base<T, E>;
3317
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3318
3319
 public:
3320
  typedef T value_type;
3321
  typedef E error_type;
3322
  typedef unexpected<E> unexpected_type;
3323
3324
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3325
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3326
  template <class F>
3327
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3328
    return and_then_impl(*this, std::forward<F>(f));
3329
  }
3330
  template <class F>
3331
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3332
    return and_then_impl(std::move(*this), std::forward<F>(f));
3333
  }
3334
  template <class F>
3335
  constexpr auto and_then(F&& f) const& {
3336
    return and_then_impl(*this, std::forward<F>(f));
3337
  }
3338
3339
#ifndef TL_EXPECTED_NO_CONSTRR
3340
  template <class F>
3341
  constexpr auto and_then(F&& f) const&& {
3342
    return and_then_impl(std::move(*this), std::forward<F>(f));
3343
  }
3344
#endif
3345
3346
#else
3347
  template <class F>
3348
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3349
      std::declval<expected&>(), std::forward<F>(f))) {
3350
    return and_then_impl(*this, std::forward<F>(f));
3351
  }
3352
  template <class F>
3353
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3354
      std::declval<expected&&>(), std::forward<F>(f))) {
3355
    return and_then_impl(std::move(*this), std::forward<F>(f));
3356
  }
3357
  template <class F>
3358
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3359
      std::declval<expected const&>(), std::forward<F>(f))) {
3360
    return and_then_impl(*this, std::forward<F>(f));
3361
  }
3362
3363
#ifndef TL_EXPECTED_NO_CONSTRR
3364
  template <class F>
3365
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3366
      std::declval<expected const&&>(), std::forward<F>(f))) {
3367
    return and_then_impl(std::move(*this), std::forward<F>(f));
3368
  }
3369
#endif
3370
#endif
3371
3372
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3373
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3374
  template <class F>
3375
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3376
    return expected_map_impl(*this, std::forward<F>(f));
3377
  }
3378
  template <class F>
3379
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3380
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3381
  }
3382
  template <class F>
3383
  constexpr auto map(F&& f) const& {
3384
    return expected_map_impl(*this, std::forward<F>(f));
3385
  }
3386
  template <class F>
3387
  constexpr auto map(F&& f) const&& {
3388
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3389
  }
3390
#else
3391
  template <class F>
3392
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3393
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3394
    return expected_map_impl(*this, std::forward<F>(f));
3395
  }
3396
  template <class F>
3397
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3398
                                                      std::declval<F&&>()))
3399
  map(F&& f) && {
3400
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3401
  }
3402
  template <class F>
3403
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3404
                                       std::declval<F&&>()))
3405
  map(F&& f) const& {
3406
    return expected_map_impl(*this, std::forward<F>(f));
3407
  }
3408
3409
#ifndef TL_EXPECTED_NO_CONSTRR
3410
  template <class F>
3411
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3412
                                       std::declval<F&&>())) map(F&& f)
3413
      const&& {
3414
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3415
  }
3416
#endif
3417
#endif
3418
3419
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3420
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3421
  template <class F>
3422
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3423
    return expected_map_impl(*this, std::forward<F>(f));
3424
  }
3425
  template <class F>
3426
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3427
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3428
  }
3429
  template <class F>
3430
  constexpr auto transform(F&& f) const& {
3431
    return expected_map_impl(*this, std::forward<F>(f));
3432
  }
3433
  template <class F>
3434
  constexpr auto transform(F&& f) const&& {
3435
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3436
  }
3437
#else
3438
  template <class F>
3439
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3440
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3441
    return expected_map_impl(*this, std::forward<F>(f));
3442
  }
3443
  template <class F>
3444
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3445
                                                      std::declval<F&&>()))
3446
  transform(F&& f) && {
3447
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3448
  }
3449
  template <class F>
3450
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3451
                                       std::declval<F&&>()))
3452
  transform(F&& f) const& {
3453
    return expected_map_impl(*this, std::forward<F>(f));
3454
  }
3455
3456
#ifndef TL_EXPECTED_NO_CONSTRR
3457
  template <class F>
3458
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3459
                                       std::declval<F&&>())) transform(F&& f)
3460
      const&& {
3461
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3462
  }
3463
#endif
3464
#endif
3465
3466
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3467
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3468
  template <class F>
3469
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3470
    return map_error_impl(*this, std::forward<F>(f));
3471
  }
3472
  template <class F>
3473
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3474
    return map_error_impl(std::move(*this), std::forward<F>(f));
3475
  }
3476
  template <class F>
3477
  constexpr auto map_error(F&& f) const& {
3478
    return map_error_impl(*this, std::forward<F>(f));
3479
  }
3480
  template <class F>
3481
  constexpr auto map_error(F&& f) const&& {
3482
    return map_error_impl(std::move(*this), std::forward<F>(f));
3483
  }
3484
#else
3485
  template <class F>
3486
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3487
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3488
    return map_error_impl(*this, std::forward<F>(f));
3489
  }
3490
  template <class F>
3491
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3492
                                                   std::declval<F&&>()))
3493
  map_error(F&& f) && {
3494
    return map_error_impl(std::move(*this), std::forward<F>(f));
3495
  }
3496
  template <class F>
3497
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3498
                                    std::declval<F&&>()))
3499
  map_error(F&& f) const& {
3500
    return map_error_impl(*this, std::forward<F>(f));
3501
  }
3502
3503
#ifndef TL_EXPECTED_NO_CONSTRR
3504
  template <class F>
3505
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3506
                                    std::declval<F&&>())) map_error(F&& f)
3507
      const&& {
3508
    return map_error_impl(std::move(*this), std::forward<F>(f));
3509
  }
3510
#endif
3511
#endif
3512
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3513
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3514
  template <class F>
3515
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3516
    return map_error_impl(*this, std::forward<F>(f));
3517
  }
3518
  template <class F>
3519
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3520
    return map_error_impl(std::move(*this), std::forward<F>(f));
3521
  }
3522
  template <class F>
3523
  constexpr auto transform_error(F&& f) const& {
3524
    return map_error_impl(*this, std::forward<F>(f));
3525
  }
3526
  template <class F>
3527
  constexpr auto transform_error(F&& f) const&& {
3528
    return map_error_impl(std::move(*this), std::forward<F>(f));
3529
  }
3530
#else
3531
  template <class F>
3532
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3533
      std::declval<expected&>(),
3534
      std::declval<F&&>())) transform_error(F&& f) & {
3535
    return map_error_impl(*this, std::forward<F>(f));
3536
  }
3537
  template <class F>
3538
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3539
                                                   std::declval<F&&>()))
3540
  transform_error(F&& f) && {
3541
    return map_error_impl(std::move(*this), std::forward<F>(f));
3542
  }
3543
  template <class F>
3544
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3545
                                    std::declval<F&&>()))
3546
  transform_error(F&& f) const& {
3547
    return map_error_impl(*this, std::forward<F>(f));
3548
  }
3549
3550
#ifndef TL_EXPECTED_NO_CONSTRR
3551
  template <class F>
3552
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3553
                                    std::declval<F&&>())) transform_error(F&& f)
3554
      const&& {
3555
    return map_error_impl(std::move(*this), std::forward<F>(f));
3556
  }
3557
#endif
3558
#endif
3559
  template <class F>
3560
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3561
    return or_else_impl(*this, std::forward<F>(f));
3562
  }
3563
3564
  template <class F>
3565
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3566
    return or_else_impl(std::move(*this), std::forward<F>(f));
3567
  }
3568
3569
  template <class F>
3570
  expected constexpr or_else(F&& f) const& {
3571
    return or_else_impl(*this, std::forward<F>(f));
3572
  }
3573
3574
#ifndef TL_EXPECTED_NO_CONSTRR
3575
  template <class F>
3576
  expected constexpr or_else(F&& f) const&& {
3577
    return or_else_impl(std::move(*this), std::forward<F>(f));
3578
  }
3579
#endif
3580
540k
  constexpr expected() = default;
3581
0
  constexpr expected(const expected& rhs) = default;
3582
  constexpr expected(expected&& rhs) = default;
3583
  expected& operator=(const expected& rhs) = default;
3584
216k
  expected& operator=(expected&& rhs) = default;
3585
3586
  template <class... Args,
3587
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3588
                nullptr>
3589
  constexpr expected(in_place_t, Args&&... args)
3590
7.16M
      : impl_base(in_place, std::forward<Args>(args)...),
3591
7.16M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3590
248k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
248k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3590
1.01M
      : impl_base(in_place, std::forward<Args>(args)...),
3591
1.01M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3590
328k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
328k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3590
855k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
855k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3590
947k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
947k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA2_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3590
58
      : impl_base(in_place, std::forward<Args>(args)...),
3591
58
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IJRS8_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
3590
996k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
996k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2IJSA_ETnPNS1_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IJS9_ETnPNS3_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__14pairINS3_17basic_string_viewIcNS3_11char_traitsIcEEEES8_EELNS1_27url_search_params_iter_typeE2EEENS1_6errorsEEC2IJSB_ETnPNS3_9enable_ifIXsr3std16is_constructibleISB_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
_ZN2tl8expectedIbN3ada6errorsEEC2IJbETnPNSt3__19enable_ifIXsr3std16is_constructibleIbDpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tES9_
Line
Count
Source
3590
324k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
324k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IJRKNS1_9nullopt_tEETnPNS1_9enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3590
385k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
385k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IJS4_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
Line
Count
Source
3590
46.6k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
46.6k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJRS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
3590
73.3k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
73.3k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IJRS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3590
917k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
917k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
3590
916k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
916k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
3590
108k
      : impl_base(in_place, std::forward<Args>(args)...),
3591
108k
        ctor_base(detail::default_constructor_tag{}) {}
3592
3593
  template <class U, class... Args,
3594
            detail::enable_if_t<std::is_constructible<
3595
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3596
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3597
      : impl_base(in_place, il, std::forward<Args>(args)...),
3598
        ctor_base(detail::default_constructor_tag{}) {}
3599
3600
  template <
3601
      class G = E,
3602
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3603
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3604
  explicit constexpr expected(const unexpected<G>& e)
3605
      : impl_base(unexpect, e.value()),
3606
        ctor_base(detail::default_constructor_tag{}) {}
3607
3608
  template <
3609
      class G = E,
3610
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3611
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3612
  constexpr expected(unexpected<G> const& e)
3613
      : impl_base(unexpect, e.value()),
3614
        ctor_base(detail::default_constructor_tag{}) {}
3615
3616
  template <
3617
      class G = E,
3618
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3619
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3620
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3621
      std::is_nothrow_constructible<E, G&&>::value)
3622
      : impl_base(unexpect, std::move(e.value())),
3623
        ctor_base(detail::default_constructor_tag{}) {}
3624
3625
  template <
3626
      class G = E,
3627
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3628
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3629
  constexpr expected(unexpected<G>&& e) noexcept(
3630
      std::is_nothrow_constructible<E, G&&>::value)
3631
713k
      : impl_base(unexpect, std::move(e.value())),
3632
713k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3631
25.3k
      : impl_base(unexpect, std::move(e.value())),
3632
25.3k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3631
530k
      : impl_base(unexpect, std::move(e.value())),
3632
530k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3631
49.7k
      : impl_base(unexpect, std::move(e.value())),
3632
49.7k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Line
Count
Source
3631
3.80k
      : impl_base(unexpect, std::move(e.value())),
3632
3.80k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Line
Count
Source
3631
1.19k
      : impl_base(unexpect, std::move(e.value())),
3632
1.19k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIbN3ada6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_OT_EE5valueEvE4typeELPv0ETnPNS6_IXsr3std14is_convertibleIS8_S2_EE5valueEvE4typeELSC_0EEEONS_10unexpectedIS7_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IS6_TnPNS1_9enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNS9_IXsr3std14is_convertibleISB_S6_EE5valueEvE4typeELSF_0EEEONS_10unexpectedISA_EE
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS6_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNSA_IXsr3std14is_convertibleISC_S6_EE5valueEvE4typeELSG_0EEEONS_10unexpectedISB_EE
Line
Count
Source
3631
91.4k
      : impl_base(unexpect, std::move(e.value())),
3632
91.4k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IS8_TnPNS1_9enable_ifIXsr3std16is_constructibleIS8_OT_EE5valueEvE4typeELPv0ETnPNSB_IXsr3std14is_convertibleISD_S8_EE5valueEvE4typeELSH_0EEEONS_10unexpectedISC_EE
Line
Count
Source
3631
5.36k
      : impl_base(unexpect, std::move(e.value())),
3632
5.36k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS6_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNSA_IXsr3std14is_convertibleISC_S6_EE5valueEvE4typeELSG_0EEEONS_10unexpectedISB_EE
Line
Count
Source
3631
6.55k
      : impl_base(unexpect, std::move(e.value())),
3632
6.55k
        ctor_base(detail::default_constructor_tag{}) {}
3633
3634
  template <class... Args,
3635
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3636
                nullptr>
3637
  constexpr explicit expected(unexpect_t, Args&&... args)
3638
      : impl_base(unexpect, std::forward<Args>(args)...),
3639
        ctor_base(detail::default_constructor_tag{}) {}
3640
3641
  template <class U, class... Args,
3642
            detail::enable_if_t<std::is_constructible<
3643
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3644
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3645
                              Args&&... args)
3646
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3647
        ctor_base(detail::default_constructor_tag{}) {}
3648
3649
  template <class U, class G,
3650
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3651
                                  std::is_convertible<G const&, E>::value)>* =
3652
                nullptr,
3653
            detail::expected_enable_from_other<T, E, U, G, const U&,
3654
                                               const G&>* = nullptr>
3655
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3656
      : ctor_base(detail::default_constructor_tag{}) {
3657
    if (rhs.has_value()) {
3658
      this->construct(*rhs);
3659
    } else {
3660
      this->construct_error(rhs.error());
3661
    }
3662
  }
3663
3664
  template <
3665
      class U, class G,
3666
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3667
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3668
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3669
          nullptr>
3670
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3671
      : ctor_base(detail::default_constructor_tag{}) {
3672
    if (rhs.has_value()) {
3673
      this->construct(*rhs);
3674
    } else {
3675
      this->construct_error(rhs.error());
3676
    }
3677
  }
3678
3679
  template <
3680
      class U, class G,
3681
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3682
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3683
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3684
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3685
      : ctor_base(detail::default_constructor_tag{}) {
3686
    if (rhs.has_value()) {
3687
      this->construct(std::move(*rhs));
3688
    } else {
3689
      this->construct_error(std::move(rhs.error()));
3690
    }
3691
  }
3692
3693
  template <
3694
      class U, class G,
3695
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3696
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3697
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3698
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3699
      : ctor_base(detail::default_constructor_tag{}) {
3700
    if (rhs.has_value()) {
3701
      this->construct(std::move(*rhs));
3702
    } else {
3703
      this->construct_error(std::move(rhs.error()));
3704
    }
3705
  }
3706
3707
  template <class U = T,
3708
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3709
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3710
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3711
      : expected(in_place, std::forward<U>(v)) {}
3712
3713
  template <class U = T,
3714
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3715
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3716
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3717
7.16M
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3717
248k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3717
1.01M
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3717
328k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3717
855k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA1_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Line
Count
Source
3717
947k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA2_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Line
Count
Source
3717
58
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IRS8_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S8_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS8_SF_EE5valuentsr3std7is_sameINS1_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SM_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESM_EE5valueEvE4typeELSJ_0EEESF_
Line
Count
Source
3717
996k
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada17url_search_paramsENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS6_IS8_EEEEN3ada6errorsEEC2ISA_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_SA_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleISA_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISD_SO_EE5valuentsr3std7is_sameINS_10unexpectedISC_EESO_EE5valueEvE4typeELSL_0EEESH_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE0EEENS1_6errorsEEC2IS9_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_S9_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS9_SF_EE5valuentsr3std7is_sameINS3_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISB_SM_EE5valuentsr3std7is_sameINS_10unexpectedISA_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__117basic_string_viewIcNS3_11char_traitsIcEEEELNS1_27url_search_params_iter_typeE1EEENS1_6errorsEEC2IS9_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_S9_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS9_SF_EE5valuentsr3std7is_sameINS3_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISB_SM_EE5valuentsr3std7is_sameINS_10unexpectedISA_EESM_EE5valueEvE4typeELSJ_0EEESF_
Unexecuted instantiation: _ZN2tl8expectedIN3ada22url_search_params_iterINSt3__14pairINS3_17basic_string_viewIcNS3_11char_traitsIcEEEES8_EELNS1_27url_search_params_iter_typeE2EEENS1_6errorsEEC2ISB_TnPNS3_9enable_ifIXsr3std14is_convertibleIOT_SB_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleISB_SH_EE5valuentsr3std7is_sameINS3_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISD_SO_EE5valuentsr3std7is_sameINS_10unexpectedISC_EESO_EE5valueEvE4typeELSL_0EEESH_
_ZN2tl8expectedIbN3ada6errorsEEC2IbTnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_bEE5valueEvE4typeELPv0ETnPNS6_IXaaaaaasr3std16is_constructibleIbS8_EE5valuentsr3std7is_sameINS5_5decayIS7_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS3_SF_EE5valuentsr3std7is_sameINS_10unexpectedIS2_EESF_EE5valueEvE4typeELSC_0EEES8_
Line
Count
Source
3717
324k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IRKNS1_9nullopt_tETnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS5_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3717
385k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IS4_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNS9_IXaaaaaasr3std16is_constructibleIS5_SB_EE5valuentsr3std7is_sameINS1_5decayISA_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SI_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESI_EE5valueEvE4typeELSF_0EEESB_
Line
Count
Source
3717
46.6k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IRS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS8_IXaaaaaasr3std16is_constructibleIS2_SA_EE5valuentsr3std7is_sameINS7_5decayIS9_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SH_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESH_EE5valueEvE4typeELSE_0EEESA_
Line
Count
Source
3717
73.3k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IRS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS9_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS8_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3717
917k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS5_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSA_IXaaaaaasr3std16is_constructibleIS5_SC_EE5valuentsr3std7is_sameINS9_5decayISB_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SJ_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESJ_EE5valueEvE4typeELSG_0EEESC_
Line
Count
Source
3717
916k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS5_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSA_IXaaaaaasr3std16is_constructibleIS5_SC_EE5valuentsr3std7is_sameINS9_5decayISB_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SJ_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESJ_EE5valueEvE4typeELSG_0EEESC_
Line
Count
Source
3717
108k
      : expected(in_place, std::forward<U>(v)) {}
3718
3719
  template <
3720
      class U = T, class G = T,
3721
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3722
          nullptr,
3723
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3724
      detail::enable_if_t<
3725
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3726
           !detail::conjunction<std::is_scalar<T>,
3727
                                std::is_same<T, detail::decay_t<U>>>::value &&
3728
           std::is_constructible<T, U>::value &&
3729
           std::is_assignable<G&, U>::value &&
3730
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3731
  expected& operator=(U&& v) {
3732
    if (has_value()) {
3733
      val() = std::forward<U>(v);
3734
    } else {
3735
      err().~unexpected<E>();
3736
      ::new (valptr()) T(std::forward<U>(v));
3737
      this->m_has_val = true;
3738
    }
3739
3740
    return *this;
3741
  }
3742
3743
  template <
3744
      class U = T, class G = T,
3745
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3746
          nullptr,
3747
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3748
      detail::enable_if_t<
3749
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3750
           !detail::conjunction<std::is_scalar<T>,
3751
                                std::is_same<T, detail::decay_t<U>>>::value &&
3752
           std::is_constructible<T, U>::value &&
3753
           std::is_assignable<G&, U>::value &&
3754
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3755
  expected& operator=(U&& v) {
3756
    if (has_value()) {
3757
      val() = std::forward<U>(v);
3758
    } else {
3759
      auto tmp = std::move(err());
3760
      err().~unexpected<E>();
3761
3762
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3763
      try {
3764
        ::new (valptr()) T(std::forward<U>(v));
3765
        this->m_has_val = true;
3766
      } catch (...) {
3767
        err() = std::move(tmp);
3768
        throw;
3769
      }
3770
#else
3771
      ::new (valptr()) T(std::forward<U>(v));
3772
      this->m_has_val = true;
3773
#endif
3774
    }
3775
3776
    return *this;
3777
  }
3778
3779
  template <class G = E,
3780
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3781
                                std::is_assignable<G&, G>::value>* = nullptr>
3782
  expected& operator=(const unexpected<G>& rhs) {
3783
    if (!has_value()) {
3784
      err() = rhs;
3785
    } else {
3786
      this->destroy_val();
3787
      ::new (errptr()) unexpected<E>(rhs);
3788
      this->m_has_val = false;
3789
    }
3790
3791
    return *this;
3792
  }
3793
3794
  template <class G = E,
3795
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3796
                                std::is_move_assignable<G>::value>* = nullptr>
3797
  expected& operator=(unexpected<G>&& rhs) noexcept {
3798
    if (!has_value()) {
3799
      err() = std::move(rhs);
3800
    } else {
3801
      this->destroy_val();
3802
      ::new (errptr()) unexpected<E>(std::move(rhs));
3803
      this->m_has_val = false;
3804
    }
3805
3806
    return *this;
3807
  }
3808
3809
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3810
                               T, Args&&...>::value>* = nullptr>
3811
  void emplace(Args&&... args) {
3812
    if (has_value()) {
3813
      val().~T();
3814
    } else {
3815
      err().~unexpected<E>();
3816
      this->m_has_val = true;
3817
    }
3818
    ::new (valptr()) T(std::forward<Args>(args)...);
3819
  }
3820
3821
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3822
                               T, Args&&...>::value>* = nullptr>
3823
  void emplace(Args&&... args) {
3824
    if (has_value()) {
3825
      val().~T();
3826
      ::new (valptr()) T(std::forward<Args>(args)...);
3827
    } else {
3828
      auto tmp = std::move(err());
3829
      err().~unexpected<E>();
3830
3831
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3832
      try {
3833
        ::new (valptr()) T(std::forward<Args>(args)...);
3834
        this->m_has_val = true;
3835
      } catch (...) {
3836
        err() = std::move(tmp);
3837
        throw;
3838
      }
3839
#else
3840
      ::new (valptr()) T(std::forward<Args>(args)...);
3841
      this->m_has_val = true;
3842
#endif
3843
    }
3844
  }
3845
3846
  template <class U, class... Args,
3847
            detail::enable_if_t<std::is_nothrow_constructible<
3848
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3849
  void emplace(std::initializer_list<U> il, Args&&... args) {
3850
    if (has_value()) {
3851
      T t(il, std::forward<Args>(args)...);
3852
      val() = std::move(t);
3853
    } else {
3854
      err().~unexpected<E>();
3855
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3856
      this->m_has_val = true;
3857
    }
3858
  }
3859
3860
  template <class U, class... Args,
3861
            detail::enable_if_t<!std::is_nothrow_constructible<
3862
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3863
  void emplace(std::initializer_list<U> il, Args&&... args) {
3864
    if (has_value()) {
3865
      T t(il, std::forward<Args>(args)...);
3866
      val() = std::move(t);
3867
    } else {
3868
      auto tmp = std::move(err());
3869
      err().~unexpected<E>();
3870
3871
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3872
      try {
3873
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3874
        this->m_has_val = true;
3875
      } catch (...) {
3876
        err() = std::move(tmp);
3877
        throw;
3878
      }
3879
#else
3880
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3881
      this->m_has_val = true;
3882
#endif
3883
    }
3884
  }
3885
3886
 private:
3887
  using t_is_void = std::true_type;
3888
  using t_is_not_void = std::false_type;
3889
  using t_is_nothrow_move_constructible = std::true_type;
3890
  using move_constructing_t_can_throw = std::false_type;
3891
  using e_is_nothrow_move_constructible = std::true_type;
3892
  using move_constructing_e_can_throw = std::false_type;
3893
3894
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3895
    // swapping void is a no-op
3896
  }
3897
3898
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3899
    using std::swap;
3900
    swap(val(), rhs.val());
3901
  }
3902
3903
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3904
      std::is_nothrow_move_constructible<E>::value) {
3905
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3906
    rhs.err().~unexpected_type();
3907
    std::swap(this->m_has_val, rhs.m_has_val);
3908
  }
3909
3910
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3911
    swap_where_only_one_has_value_and_t_is_not_void(
3912
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3913
        typename std::is_nothrow_move_constructible<E>::type{});
3914
  }
3915
3916
  void swap_where_only_one_has_value_and_t_is_not_void(
3917
      expected& rhs, t_is_nothrow_move_constructible,
3918
      e_is_nothrow_move_constructible) noexcept {
3919
    auto temp = std::move(val());
3920
    val().~T();
3921
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3922
    rhs.err().~unexpected_type();
3923
    ::new (rhs.valptr()) T(std::move(temp));
3924
    std::swap(this->m_has_val, rhs.m_has_val);
3925
  }
3926
3927
  void swap_where_only_one_has_value_and_t_is_not_void(
3928
      expected& rhs, t_is_nothrow_move_constructible,
3929
      move_constructing_e_can_throw) {
3930
    auto temp = std::move(val());
3931
    val().~T();
3932
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3933
    try {
3934
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
3935
      rhs.err().~unexpected_type();
3936
      ::new (rhs.valptr()) T(std::move(temp));
3937
      std::swap(this->m_has_val, rhs.m_has_val);
3938
    } catch (...) {
3939
      val() = std::move(temp);
3940
      throw;
3941
    }
3942
#else
3943
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3944
    rhs.err().~unexpected_type();
3945
    ::new (rhs.valptr()) T(std::move(temp));
3946
    std::swap(this->m_has_val, rhs.m_has_val);
3947
#endif
3948
  }
3949
3950
  void swap_where_only_one_has_value_and_t_is_not_void(
3951
      expected& rhs, move_constructing_t_can_throw,
3952
      e_is_nothrow_move_constructible) {
3953
    auto temp = std::move(rhs.err());
3954
    rhs.err().~unexpected_type();
3955
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3956
    try {
3957
      ::new (rhs.valptr()) T(std::move(val()));
3958
      val().~T();
3959
      ::new (errptr()) unexpected_type(std::move(temp));
3960
      std::swap(this->m_has_val, rhs.m_has_val);
3961
    } catch (...) {
3962
      rhs.err() = std::move(temp);
3963
      throw;
3964
    }
3965
#else
3966
    ::new (rhs.valptr()) T(std::move(val()));
3967
    val().~T();
3968
    ::new (errptr()) unexpected_type(std::move(temp));
3969
    std::swap(this->m_has_val, rhs.m_has_val);
3970
#endif
3971
  }
3972
3973
 public:
3974
  template <class OT = T, class OE = E>
3975
  detail::enable_if_t<detail::is_swappable<OT>::value &&
3976
                      detail::is_swappable<OE>::value &&
3977
                      (std::is_nothrow_move_constructible<OT>::value ||
3978
                       std::is_nothrow_move_constructible<OE>::value)>
3979
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
3980
                               detail::is_nothrow_swappable<T>::value &&
3981
                               std::is_nothrow_move_constructible<E>::value &&
3982
                               detail::is_nothrow_swappable<E>::value) {
3983
    if (has_value() && rhs.has_value()) {
3984
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
3985
    } else if (!has_value() && rhs.has_value()) {
3986
      rhs.swap(*this);
3987
    } else if (has_value()) {
3988
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
3989
    } else {
3990
      using std::swap;
3991
      swap(err(), rhs.err());
3992
    }
3993
  }
3994
3995
  constexpr const T* operator->() const {
3996
    TL_ASSERT(has_value());
3997
    return valptr();
3998
  }
3999
11.4M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4000
11.4M
    TL_ASSERT(has_value());
4001
11.4M
    return valptr();
4002
11.4M
  }
tl::expected<ada::url, ada::errors>::operator->()
Line
Count
Source
3999
905k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4000
905k
    TL_ASSERT(has_value());
4001
905k
    return valptr();
4002
905k
  }
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
3999
3.38M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4000
3.38M
    TL_ASSERT(has_value());
4001
3.38M
    return valptr();
4002
3.38M
  }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::operator->()
tl::expected<ada::url_pattern_init, ada::errors>::operator->()
Line
Count
Source
3999
4.42M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4000
4.42M
    TL_ASSERT(has_value());
4001
4.42M
    return valptr();
4002
4.42M
  }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::operator->()
Line
Count
Source
3999
648k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4000
648k
    TL_ASSERT(has_value());
4001
648k
    return valptr();
4002
648k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator->()
Line
Count
Source
3999
2.06M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4000
2.06M
    TL_ASSERT(has_value());
4001
2.06M
    return valptr();
4002
2.06M
  }
4003
4004
  template <class U = T,
4005
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4006
  constexpr const U& operator*() const& {
4007
    TL_ASSERT(has_value());
4008
    return val();
4009
  }
4010
  template <class U = T,
4011
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4012
8.72M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
8.72M
    TL_ASSERT(has_value());
4014
8.72M
    return val();
4015
8.72M
  }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4012
356k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
356k
    TL_ASSERT(has_value());
4014
356k
    return val();
4015
356k
  }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4012
982k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
982k
    TL_ASSERT(has_value());
4014
982k
    return val();
4015
982k
  }
_ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
4012
1.80M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
1.80M
    TL_ASSERT(has_value());
4014
1.80M
    return val();
4015
1.80M
  }
_ZNR2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
4012
216k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
216k
    TL_ASSERT(has_value());
4014
216k
    return val();
4015
216k
  }
_ZNR2tl8expectedIbN3ada6errorsEEdeIbTnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS7_v
Line
Count
Source
4012
324k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
324k
    TL_ASSERT(has_value());
4014
324k
    return val();
4015
324k
  }
_ZNR2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEdeIS5_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSA_v
Line
Count
Source
4012
38.8k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
38.8k
    TL_ASSERT(has_value());
4014
38.8k
    return val();
4015
38.8k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
4012
996k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
996k
    TL_ASSERT(has_value());
4014
996k
    return val();
4015
996k
  }
_ZNR2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4012
73.3k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
73.3k
    TL_ASSERT(has_value());
4014
73.3k
    return val();
4015
73.3k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
4012
3.01M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
3.01M
    TL_ASSERT(has_value());
4014
3.01M
    return val();
4015
3.01M
  }
_ZNR2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
4012
916k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4013
916k
    TL_ASSERT(has_value());
4014
916k
    return val();
4015
916k
  }
4016
  template <class U = T,
4017
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4018
  constexpr const U&& operator*() const&& {
4019
    TL_ASSERT(has_value());
4020
    return std::move(val());
4021
  }
4022
  template <class U = T,
4023
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4024
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4025
    TL_ASSERT(has_value());
4026
    return std::move(val());
4027
  }
4028
4029
21.1M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::has_value() const
Line
Count
Source
4029
1.42M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
4029
5.06M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::has_value() const
Line
Count
Source
4029
1.80M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::has_value() const
tl::expected<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors>::has_value() const
Line
Count
Source
4029
216k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_init, ada::errors>::has_value() const
Line
Count
Source
4029
4.65M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::has_value() const
Line
Count
Source
4029
687k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<bool, ada::errors>::has_value() const
Line
Count
Source
4029
324k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::has_value() const
Line
Count
Source
4029
997k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::has_value() const
Line
Count
Source
4029
5.08M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::has_value() const
Line
Count
Source
4029
922k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
4030
7.75M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url, ada::errors>::operator bool() const
Line
Count
Source
4030
299k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
4030
1.29M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::operator bool() const
Line
Count
Source
4030
1.80M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator bool() const
tl::expected<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors>::operator bool() const
Line
Count
Source
4030
199k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_init, ada::errors>::operator bool() const
Line
Count
Source
4030
343k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::operator bool() const
Line
Count
Source
4030
648k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<bool, ada::errors>::operator bool() const
Line
Count
Source
4030
324k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::operator bool() const
Line
Count
Source
4030
997k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator bool() const
Line
Count
Source
4030
922k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::operator bool() const
Line
Count
Source
4030
922k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
4031
4032
  template <class U = T,
4033
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4034
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4035
    if (!has_value())
4036
      detail::throw_exception(bad_expected_access<E>(err().value()));
4037
    return val();
4038
  }
4039
  template <class U = T,
4040
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4041
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4042
0
    if (!has_value())
4043
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4044
0
    return val();
4045
0
  }
4046
  template <class U = T,
4047
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4048
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4049
    if (!has_value())
4050
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4051
    return std::move(val());
4052
  }
4053
  template <class U = T,
4054
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4055
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4056
    if (!has_value())
4057
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4058
    return std::move(val());
4059
  }
4060
4061
  constexpr const E& error() const& {
4062
    TL_ASSERT(!has_value());
4063
    return err().value();
4064
  }
4065
66.2k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4066
66.2k
    TL_ASSERT(!has_value());
4067
66.2k
    return err().value();
4068
66.2k
  }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Line
Count
Source
4065
3.80k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4066
3.80k
    TL_ASSERT(!has_value());
4067
3.80k
    return err().value();
4068
3.80k
  }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
Line
Count
Source
4065
1.19k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4066
1.19k
    TL_ASSERT(!has_value());
4067
1.19k
    return err().value();
4068
1.19k
  }
tl::expected<ada::url_pattern_init, ada::errors>::error() &
Line
Count
Source
4065
49.3k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4066
49.3k
    TL_ASSERT(!has_value());
4067
49.3k
    return err().value();
4068
49.3k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::error() &
Line
Count
Source
4065
5.36k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4066
5.36k
    TL_ASSERT(!has_value());
4067
5.36k
    return err().value();
4068
5.36k
  }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::error() &
Line
Count
Source
4065
6.55k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4066
6.55k
    TL_ASSERT(!has_value());
4067
6.55k
    return err().value();
4068
6.55k
  }
4069
  constexpr const E&& error() const&& {
4070
    TL_ASSERT(!has_value());
4071
    return std::move(err().value());
4072
  }
4073
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4074
    TL_ASSERT(!has_value());
4075
    return std::move(err().value());
4076
  }
4077
4078
  template <class U>
4079
  constexpr T value_or(U&& v) const& {
4080
    static_assert(std::is_copy_constructible<T>::value &&
4081
                      std::is_convertible<U&&, T>::value,
4082
                  "T must be copy-constructible and convertible to from U&&");
4083
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4084
  }
4085
  template <class U>
4086
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4087
    static_assert(std::is_move_constructible<T>::value &&
4088
                      std::is_convertible<U&&, T>::value,
4089
                  "T must be move-constructible and convertible to from U&&");
4090
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4091
  }
4092
};
4093
4094
namespace detail {
4095
template <class Exp>
4096
using exp_t = typename detail::decay_t<Exp>::value_type;
4097
template <class Exp>
4098
using err_t = typename detail::decay_t<Exp>::error_type;
4099
template <class Exp, class Ret>
4100
using ret_t = expected<Ret, err_t<Exp>>;
4101
4102
#ifdef TL_EXPECTED_CXX14
4103
template <class Exp, class F,
4104
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4105
          class Ret = decltype(detail::invoke(std::declval<F>(),
4106
                                              *std::declval<Exp>()))>
4107
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4108
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4109
4110
  return exp.has_value()
4111
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4112
             : Ret(unexpect, std::forward<Exp>(exp).error());
4113
}
4114
4115
template <class Exp, class F,
4116
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4117
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4118
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4119
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4120
4121
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4122
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4123
}
4124
#else
4125
template <class>
4126
struct TC;
4127
template <class Exp, class F,
4128
          class Ret = decltype(detail::invoke(std::declval<F>(),
4129
                                              *std::declval<Exp>())),
4130
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4131
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4132
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4133
4134
  return exp.has_value()
4135
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4136
             : Ret(unexpect, std::forward<Exp>(exp).error());
4137
}
4138
4139
template <class Exp, class F,
4140
          class Ret = decltype(detail::invoke(std::declval<F>())),
4141
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4142
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4143
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4144
4145
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4146
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4147
}
4148
#endif
4149
4150
#ifdef TL_EXPECTED_CXX14
4151
template <class Exp, class F,
4152
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4153
          class Ret = decltype(detail::invoke(std::declval<F>(),
4154
                                              *std::declval<Exp>())),
4155
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4156
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4157
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4158
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4159
                                                 *std::forward<Exp>(exp)))
4160
                         : result(unexpect, std::forward<Exp>(exp).error());
4161
}
4162
4163
template <class Exp, class F,
4164
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4165
          class Ret = decltype(detail::invoke(std::declval<F>(),
4166
                                              *std::declval<Exp>())),
4167
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4168
auto expected_map_impl(Exp&& exp, F&& f) {
4169
  using result = expected<void, err_t<Exp>>;
4170
  if (exp.has_value()) {
4171
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4172
    return result();
4173
  }
4174
4175
  return result(unexpect, std::forward<Exp>(exp).error());
4176
}
4177
4178
template <class Exp, class F,
4179
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4180
          class Ret = decltype(detail::invoke(std::declval<F>())),
4181
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4182
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4183
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4184
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4185
                         : result(unexpect, std::forward<Exp>(exp).error());
4186
}
4187
4188
template <class Exp, class F,
4189
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4190
          class Ret = decltype(detail::invoke(std::declval<F>())),
4191
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4192
auto expected_map_impl(Exp&& exp, F&& f) {
4193
  using result = expected<void, err_t<Exp>>;
4194
  if (exp.has_value()) {
4195
    detail::invoke(std::forward<F>(f));
4196
    return result();
4197
  }
4198
4199
  return result(unexpect, std::forward<Exp>(exp).error());
4200
}
4201
#else
4202
template <class Exp, class F,
4203
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4204
          class Ret = decltype(detail::invoke(std::declval<F>(),
4205
                                              *std::declval<Exp>())),
4206
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4207
4208
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4209
    -> ret_t<Exp, detail::decay_t<Ret>> {
4210
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4211
4212
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4213
                                                 *std::forward<Exp>(exp)))
4214
                         : result(unexpect, std::forward<Exp>(exp).error());
4215
}
4216
4217
template <class Exp, class F,
4218
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4219
          class Ret = decltype(detail::invoke(std::declval<F>(),
4220
                                              *std::declval<Exp>())),
4221
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4222
4223
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4224
  if (exp.has_value()) {
4225
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4226
    return {};
4227
  }
4228
4229
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4230
}
4231
4232
template <class Exp, class F,
4233
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4234
          class Ret = decltype(detail::invoke(std::declval<F>())),
4235
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4236
4237
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4238
    -> ret_t<Exp, detail::decay_t<Ret>> {
4239
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4240
4241
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4242
                         : result(unexpect, std::forward<Exp>(exp).error());
4243
}
4244
4245
template <class Exp, class F,
4246
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4247
          class Ret = decltype(detail::invoke(std::declval<F>())),
4248
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4249
4250
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4251
  if (exp.has_value()) {
4252
    detail::invoke(std::forward<F>(f));
4253
    return {};
4254
  }
4255
4256
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4257
}
4258
#endif
4259
4260
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4261
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4262
template <class Exp, class F,
4263
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4264
          class Ret = decltype(detail::invoke(std::declval<F>(),
4265
                                              std::declval<Exp>().error())),
4266
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4267
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4268
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4269
  return exp.has_value()
4270
             ? result(*std::forward<Exp>(exp))
4271
             : result(unexpect, detail::invoke(std::forward<F>(f),
4272
                                               std::forward<Exp>(exp).error()));
4273
}
4274
template <class Exp, class F,
4275
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4276
          class Ret = decltype(detail::invoke(std::declval<F>(),
4277
                                              std::declval<Exp>().error())),
4278
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4279
auto map_error_impl(Exp&& exp, F&& f) {
4280
  using result = expected<exp_t<Exp>, monostate>;
4281
  if (exp.has_value()) {
4282
    return result(*std::forward<Exp>(exp));
4283
  }
4284
4285
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4286
  return result(unexpect, monostate{});
4287
}
4288
template <class Exp, class F,
4289
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4290
          class Ret = decltype(detail::invoke(std::declval<F>(),
4291
                                              std::declval<Exp>().error())),
4292
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4293
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4294
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4295
  return exp.has_value()
4296
             ? result()
4297
             : result(unexpect, detail::invoke(std::forward<F>(f),
4298
                                               std::forward<Exp>(exp).error()));
4299
}
4300
template <class Exp, class F,
4301
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4302
          class Ret = decltype(detail::invoke(std::declval<F>(),
4303
                                              std::declval<Exp>().error())),
4304
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4305
auto map_error_impl(Exp&& exp, F&& f) {
4306
  using result = expected<exp_t<Exp>, monostate>;
4307
  if (exp.has_value()) {
4308
    return result();
4309
  }
4310
4311
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4312
  return result(unexpect, monostate{});
4313
}
4314
#else
4315
template <class Exp, class F,
4316
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4317
          class Ret = decltype(detail::invoke(std::declval<F>(),
4318
                                              std::declval<Exp>().error())),
4319
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4320
constexpr auto map_error_impl(Exp&& exp, F&& f)
4321
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4322
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4323
4324
  return exp.has_value()
4325
             ? result(*std::forward<Exp>(exp))
4326
             : result(unexpect, detail::invoke(std::forward<F>(f),
4327
                                               std::forward<Exp>(exp).error()));
4328
}
4329
4330
template <class Exp, class F,
4331
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4332
          class Ret = decltype(detail::invoke(std::declval<F>(),
4333
                                              std::declval<Exp>().error())),
4334
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4335
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4336
  using result = expected<exp_t<Exp>, monostate>;
4337
  if (exp.has_value()) {
4338
    return result(*std::forward<Exp>(exp));
4339
  }
4340
4341
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4342
  return result(unexpect, monostate{});
4343
}
4344
4345
template <class Exp, class F,
4346
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4347
          class Ret = decltype(detail::invoke(std::declval<F>(),
4348
                                              std::declval<Exp>().error())),
4349
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4350
constexpr auto map_error_impl(Exp&& exp, F&& f)
4351
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4352
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4353
4354
  return exp.has_value()
4355
             ? result()
4356
             : result(unexpect, detail::invoke(std::forward<F>(f),
4357
                                               std::forward<Exp>(exp).error()));
4358
}
4359
4360
template <class Exp, class F,
4361
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4362
          class Ret = decltype(detail::invoke(std::declval<F>(),
4363
                                              std::declval<Exp>().error())),
4364
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4365
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4366
  using result = expected<exp_t<Exp>, monostate>;
4367
  if (exp.has_value()) {
4368
    return result();
4369
  }
4370
4371
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4372
  return result(unexpect, monostate{});
4373
}
4374
#endif
4375
4376
#ifdef TL_EXPECTED_CXX14
4377
template <class Exp, class F,
4378
          class Ret = decltype(detail::invoke(std::declval<F>(),
4379
                                              std::declval<Exp>().error())),
4380
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4381
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4382
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4383
  return exp.has_value() ? std::forward<Exp>(exp)
4384
                         : detail::invoke(std::forward<F>(f),
4385
                                          std::forward<Exp>(exp).error());
4386
}
4387
4388
template <class Exp, class F,
4389
          class Ret = decltype(detail::invoke(std::declval<F>(),
4390
                                              std::declval<Exp>().error())),
4391
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4392
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4393
  return exp.has_value() ? std::forward<Exp>(exp)
4394
                         : (detail::invoke(std::forward<F>(f),
4395
                                           std::forward<Exp>(exp).error()),
4396
                            std::forward<Exp>(exp));
4397
}
4398
#else
4399
template <class Exp, class F,
4400
          class Ret = decltype(detail::invoke(std::declval<F>(),
4401
                                              std::declval<Exp>().error())),
4402
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4403
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4404
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4405
  return exp.has_value() ? std::forward<Exp>(exp)
4406
                         : detail::invoke(std::forward<F>(f),
4407
                                          std::forward<Exp>(exp).error());
4408
}
4409
4410
template <class Exp, class F,
4411
          class Ret = decltype(detail::invoke(std::declval<F>(),
4412
                                              std::declval<Exp>().error())),
4413
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4414
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4415
  return exp.has_value() ? std::forward<Exp>(exp)
4416
                         : (detail::invoke(std::forward<F>(f),
4417
                                           std::forward<Exp>(exp).error()),
4418
                            std::forward<Exp>(exp));
4419
}
4420
#endif
4421
}  // namespace detail
4422
4423
template <class T, class E, class U, class F>
4424
constexpr bool operator==(const expected<T, E>& lhs,
4425
                          const expected<U, F>& rhs) {
4426
  return (lhs.has_value() != rhs.has_value())
4427
             ? false
4428
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4429
}
4430
template <class T, class E, class U, class F>
4431
constexpr bool operator!=(const expected<T, E>& lhs,
4432
                          const expected<U, F>& rhs) {
4433
  return (lhs.has_value() != rhs.has_value())
4434
             ? true
4435
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4436
}
4437
template <class E, class F>
4438
constexpr bool operator==(const expected<void, E>& lhs,
4439
                          const expected<void, F>& rhs) {
4440
  return (lhs.has_value() != rhs.has_value())
4441
             ? false
4442
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4443
}
4444
template <class E, class F>
4445
constexpr bool operator!=(const expected<void, E>& lhs,
4446
                          const expected<void, F>& rhs) {
4447
  return (lhs.has_value() != rhs.has_value())
4448
             ? true
4449
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4450
}
4451
4452
template <class T, class E, class U>
4453
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4454
  return x.has_value() ? *x == v : false;
4455
}
4456
template <class T, class E, class U>
4457
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4458
  return x.has_value() ? *x == v : false;
4459
}
4460
template <class T, class E, class U>
4461
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4462
  return x.has_value() ? *x != v : true;
4463
}
4464
template <class T, class E, class U>
4465
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4466
  return x.has_value() ? *x != v : true;
4467
}
4468
4469
template <class T, class E>
4470
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4471
  return x.has_value() ? false : x.error() == e.value();
4472
}
4473
template <class T, class E>
4474
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4475
  return x.has_value() ? false : x.error() == e.value();
4476
}
4477
template <class T, class E>
4478
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4479
  return x.has_value() ? true : x.error() != e.value();
4480
}
4481
template <class T, class E>
4482
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4483
  return x.has_value() ? true : x.error() != e.value();
4484
}
4485
4486
template <class T, class E,
4487
          detail::enable_if_t<(std::is_void<T>::value ||
4488
                               std::is_move_constructible<T>::value) &&
4489
                              detail::is_swappable<T>::value &&
4490
                              std::is_move_constructible<E>::value &&
4491
                              detail::is_swappable<E>::value>* = nullptr>
4492
void swap(expected<T, E>& lhs,
4493
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4494
  lhs.swap(rhs);
4495
}
4496
}  // namespace tl
4497
4498
#endif
4499
/* end file include/ada/expected.h */
4500
4501
/* begin file include/ada/url_pattern_regex.h */
4502
/**
4503
 * @file url_search_params.h
4504
 * @brief Declaration for the URL Search Params
4505
 */
4506
#ifndef ADA_URL_PATTERN_REGEX_H
4507
#define ADA_URL_PATTERN_REGEX_H
4508
4509
#include <string>
4510
#include <string_view>
4511
4512
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4513
#include <regex>
4514
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4515
4516
#if ADA_INCLUDE_URL_PATTERN
4517
namespace ada::url_pattern_regex {
4518
4519
template <typename T>
4520
concept regex_concept = requires(T t, std::string_view pattern,
4521
                                 bool ignore_case, std::string_view input) {
4522
  // Ensure the class has a type alias 'regex_type'
4523
  typename T::regex_type;
4524
4525
  // Function to create a regex instance
4526
  {
4527
    T::create_instance(pattern, ignore_case)
4528
  } -> std::same_as<std::optional<typename T::regex_type>>;
4529
4530
  // Function to perform regex search
4531
  {
4532
    T::regex_search(input, std::declval<typename T::regex_type&>())
4533
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4534
4535
  // Function to match regex pattern
4536
  {
4537
    T::regex_match(input, std::declval<typename T::regex_type&>())
4538
  } -> std::same_as<bool>;
4539
4540
  // Copy constructor
4541
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4542
4543
  // Move constructor
4544
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4545
};
4546
4547
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4548
class std_regex_provider final {
4549
 public:
4550
  std_regex_provider() = default;
4551
  using regex_type = std::regex;
4552
  static std::optional<regex_type> create_instance(std::string_view pattern,
4553
                                                   bool ignore_case);
4554
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4555
      std::string_view input, const regex_type& pattern);
4556
  static bool regex_match(std::string_view input, const regex_type& pattern);
4557
};
4558
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4559
4560
}  // namespace ada::url_pattern_regex
4561
#endif  // ADA_INCLUDE_URL_PATTERN
4562
#endif  // ADA_URL_PATTERN_REGEX_H
4563
/* end file include/ada/url_pattern_regex.h */
4564
/* begin file include/ada/url_pattern_init.h */
4565
/**
4566
 * @file url_pattern_init.h
4567
 * @brief Declaration for the url_pattern_init implementation.
4568
 */
4569
#ifndef ADA_URL_PATTERN_INIT_H
4570
#define ADA_URL_PATTERN_INIT_H
4571
4572
/* begin file include/ada/errors.h */
4573
/**
4574
 * @file errors.h
4575
 * @brief Error type definitions for URL parsing.
4576
 *
4577
 * Defines the error codes that can be returned when URL parsing fails.
4578
 */
4579
#ifndef ADA_ERRORS_H
4580
#define ADA_ERRORS_H
4581
4582
#include <cstdint>
4583
namespace ada {
4584
/**
4585
 * @brief Error codes for URL parsing operations.
4586
 *
4587
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4588
 */
4589
enum class errors : uint8_t {
4590
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4591
};
4592
}  // namespace ada
4593
#endif  // ADA_ERRORS_H
4594
/* end file include/ada/errors.h */
4595
4596
#include <string_view>
4597
#include <string>
4598
#include <optional>
4599
#include <iostream>
4600
4601
#if ADA_TESTING
4602
#include <iostream>
4603
#endif  // ADA_TESTING
4604
4605
#if ADA_INCLUDE_URL_PATTERN
4606
namespace ada {
4607
4608
// Important: C++20 allows us to use concept rather than `using` or `typedef
4609
// and allows functions with second argument, which is optional (using either
4610
// std::nullopt or a parameter with default value)
4611
template <typename F>
4612
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4613
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4614
};
4615
4616
// A structure providing matching patterns for individual components
4617
// of a URL. When a URLPattern is created, or when a URLPattern is
4618
// used to match or test against a URL, the input can be given as
4619
// either a string or a URLPatternInit struct. If a string is given,
4620
// it will be parsed to create a URLPatternInit. The URLPatternInit
4621
// API is defined as part of the URLPattern specification.
4622
// All provided strings must be valid UTF-8.
4623
struct url_pattern_init {
4624
  enum class process_type : uint8_t {
4625
    url,
4626
    pattern,
4627
  };
4628
4629
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4630
0
    switch (type) {
4631
0
      case process_type::url:
4632
0
        return os << "url";
4633
0
      case process_type::pattern:
4634
0
        return os << "pattern";
4635
0
      default:
4636
0
        return os << "unknown";
4637
0
    }
4638
0
  }
4639
4640
  // All strings must be valid UTF-8.
4641
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4642
  static tl::expected<url_pattern_init, errors> process(
4643
      const url_pattern_init& init, process_type type,
4644
      std::optional<std::string_view> protocol = std::nullopt,
4645
      std::optional<std::string_view> username = std::nullopt,
4646
      std::optional<std::string_view> password = std::nullopt,
4647
      std::optional<std::string_view> hostname = std::nullopt,
4648
      std::optional<std::string_view> port = std::nullopt,
4649
      std::optional<std::string_view> pathname = std::nullopt,
4650
      std::optional<std::string_view> search = std::nullopt,
4651
      std::optional<std::string_view> hash = std::nullopt);
4652
4653
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4654
  static tl::expected<std::string, errors> process_protocol(
4655
      std::string_view value, process_type type);
4656
4657
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4658
  static tl::expected<std::string, errors> process_username(
4659
      std::string_view value, process_type type);
4660
4661
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4662
  static tl::expected<std::string, errors> process_password(
4663
      std::string_view value, process_type type);
4664
4665
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4666
  static tl::expected<std::string, errors> process_hostname(
4667
      std::string_view value, process_type type);
4668
4669
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4670
  static tl::expected<std::string, errors> process_port(
4671
      std::string_view port, std::string_view protocol, process_type type);
4672
4673
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4674
  static tl::expected<std::string, errors> process_pathname(
4675
      std::string_view value, std::string_view protocol, process_type type);
4676
4677
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4678
  static tl::expected<std::string, errors> process_search(
4679
      std::string_view value, process_type type);
4680
4681
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4682
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4683
                                                        process_type type);
4684
4685
#if ADA_TESTING
4686
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4687
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4688
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4689
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4690
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4691
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4692
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4693
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4694
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4695
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4696
  }
4697
#endif  // ADA_TESTING
4698
4699
  bool operator==(const url_pattern_init&) const;
4700
  // If present, must be valid UTF-8.
4701
  std::optional<std::string> protocol{};
4702
  // If present, must be valid UTF-8.
4703
  std::optional<std::string> username{};
4704
  // If present, must be valid UTF-8.
4705
  std::optional<std::string> password{};
4706
  // If present, must be valid UTF-8.
4707
  std::optional<std::string> hostname{};
4708
  // If present, must be valid UTF-8.
4709
  std::optional<std::string> port{};
4710
  // If present, must be valid UTF-8.
4711
  std::optional<std::string> pathname{};
4712
  // If present, must be valid UTF-8.
4713
  std::optional<std::string> search{};
4714
  // If present, must be valid UTF-8.
4715
  std::optional<std::string> hash{};
4716
  // If present, must be valid UTF-8.
4717
  std::optional<std::string> base_url{};
4718
};
4719
}  // namespace ada
4720
#endif  // ADA_INCLUDE_URL_PATTERN
4721
#endif  // ADA_URL_PATTERN_INIT_H
4722
/* end file include/ada/url_pattern_init.h */
4723
4724
/** @private Forward declarations */
4725
namespace ada {
4726
struct url_aggregator;
4727
struct url;
4728
#if ADA_INCLUDE_URL_PATTERN
4729
template <url_pattern_regex::regex_concept regex_provider>
4730
class url_pattern;
4731
struct url_pattern_options;
4732
#endif  // ADA_INCLUDE_URL_PATTERN
4733
enum class errors : uint8_t;
4734
}  // namespace ada
4735
4736
/**
4737
 * @namespace ada::parser
4738
 * @brief Internal URL parsing implementation.
4739
 *
4740
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4741
 * Standard. These functions are used internally by `ada::parse()`.
4742
 */
4743
namespace ada::parser {
4744
/**
4745
 * Parses a URL string into a URL object.
4746
 *
4747
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4748
 *
4749
 * @param user_input The URL string to parse (must be valid UTF-8).
4750
 * @param base_url Optional base URL for resolving relative URLs.
4751
 *
4752
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4753
 *         succeeded.
4754
 *
4755
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4756
 */
4757
template <typename result_type = url_aggregator>
4758
result_type parse_url(std::string_view user_input,
4759
                      const result_type* base_url = nullptr);
4760
4761
extern template url_aggregator parse_url<url_aggregator>(
4762
    std::string_view user_input, const url_aggregator* base_url);
4763
extern template url parse_url<url>(std::string_view user_input,
4764
                                   const url* base_url);
4765
4766
template <typename result_type = url_aggregator, bool store_values = true>
4767
result_type parse_url_impl(std::string_view user_input,
4768
                           const result_type* base_url = nullptr);
4769
4770
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4771
    std::string_view user_input, const url_aggregator* base_url);
4772
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4773
    std::string_view user_input, const url_aggregator* base_url);
4774
extern template url parse_url_impl<url, true>(std::string_view user_input,
4775
                                              const url* base_url);
4776
4777
/** @private */
4778
template <class result_type>
4779
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4780
4781
#if ADA_INCLUDE_URL_PATTERN
4782
template <url_pattern_regex::regex_concept regex_provider>
4783
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4784
    std::variant<std::string_view, url_pattern_init>&& input,
4785
    const std::string_view* base_url, const url_pattern_options* options);
4786
#endif  // ADA_INCLUDE_URL_PATTERN
4787
4788
}  // namespace ada::parser
4789
4790
#endif  // ADA_PARSER_H
4791
/* end file include/ada/parser.h */
4792
/* begin file include/ada/parser-inl.h */
4793
/**
4794
 * @file parser-inl.h
4795
 */
4796
#ifndef ADA_PARSER_INL_H
4797
#define ADA_PARSER_INL_H
4798
4799
/* begin file include/ada/url_pattern.h */
4800
/**
4801
 * @file url_pattern.h
4802
 * @brief URLPattern API implementation.
4803
 *
4804
 * This header provides the URLPattern API as specified by the WHATWG URL
4805
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4806
 * wildcards and named groups, similar to how regular expressions match strings.
4807
 *
4808
 * @see https://urlpattern.spec.whatwg.org/
4809
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4810
 */
4811
#ifndef ADA_URL_PATTERN_H
4812
#define ADA_URL_PATTERN_H
4813
4814
/* begin file include/ada/implementation.h */
4815
/**
4816
 * @file implementation.h
4817
 * @brief User-facing functions for URL parsing and manipulation.
4818
 *
4819
 * This header provides the primary public API for parsing URLs in Ada.
4820
 * It includes the main `ada::parse()` function which is the recommended
4821
 * entry point for most users.
4822
 *
4823
 * @see https://url.spec.whatwg.org/#api
4824
 */
4825
#ifndef ADA_IMPLEMENTATION_H
4826
#define ADA_IMPLEMENTATION_H
4827
4828
#include <string>
4829
#include <string_view>
4830
#include <optional>
4831
4832
/* begin file include/ada/url.h */
4833
/**
4834
 * @file url.h
4835
 * @brief Declaration for the `ada::url` class.
4836
 *
4837
 * This file contains the `ada::url` struct which represents a parsed URL
4838
 * using separate `std::string` instances for each component. This
4839
 * representation is more flexible but uses more memory than `url_aggregator`.
4840
 *
4841
 * @see url_aggregator.h for a more memory-efficient alternative
4842
 */
4843
#ifndef ADA_URL_H
4844
#define ADA_URL_H
4845
4846
#include <algorithm>
4847
#include <optional>
4848
#include <ostream>
4849
#include <string>
4850
#include <string_view>
4851
4852
/* begin file include/ada/url_components.h */
4853
/**
4854
 * @file url_components.h
4855
 * @brief URL component offset representation for url_aggregator.
4856
 *
4857
 * This file defines the `url_components` struct which stores byte offsets
4858
 * into a URL string buffer. It is used internally by `url_aggregator` to
4859
 * efficiently locate URL components without storing separate strings.
4860
 */
4861
#ifndef ADA_URL_COMPONENTS_H
4862
#define ADA_URL_COMPONENTS_H
4863
4864
namespace ada {
4865
4866
/**
4867
 * @brief Stores byte offsets for URL components within a buffer.
4868
 *
4869
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4870
 * of each URL component within a single string buffer. This enables efficient
4871
 * component extraction without additional memory allocations.
4872
 *
4873
 * Component layout in a URL:
4874
 * ```
4875
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4876
 *       |     |    |          | ^^^^|       |   |
4877
 *       |     |    |          | |   |       |   `----- hash_start
4878
 *       |     |    |          | |   |       `--------- search_start
4879
 *       |     |    |          | |   `----------------- pathname_start
4880
 *       |     |    |          | `--------------------- port
4881
 *       |     |    |          `----------------------- host_end
4882
 *       |     |    `---------------------------------- host_start
4883
 *       |     `--------------------------------------- username_end
4884
 *       `--------------------------------------------- protocol_end
4885
 * ```
4886
 *
4887
 * @note The 32-bit offsets limit URLs to 4GB in length.
4888
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
4889
 * present.
4890
 */
4891
struct url_components {
4892
  /** Sentinel value indicating a component is not present. */
4893
  constexpr static uint32_t omitted = uint32_t(-1);
4894
4895
2.11M
  url_components() = default;
4896
  url_components(const url_components& u) = default;
4897
  url_components(url_components&& u) noexcept = default;
4898
  url_components& operator=(url_components&& u) noexcept = default;
4899
  url_components& operator=(const url_components& u) = default;
4900
  ~url_components() = default;
4901
4902
  /** Offset of the end of the protocol/scheme (position of ':'). */
4903
  uint32_t protocol_end{0};
4904
4905
  /**
4906
   * Offset of the end of the username.
4907
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
4908
   */
4909
  uint32_t username_end{0};
4910
4911
  /** Offset of the start of the host. */
4912
  uint32_t host_start{0};
4913
4914
  /** Offset of the end of the host. */
4915
  uint32_t host_end{0};
4916
4917
  /** Port number, or `omitted` if no port is specified. */
4918
  uint32_t port{omitted};
4919
4920
  /** Offset of the start of the pathname. */
4921
  uint32_t pathname_start{0};
4922
4923
  /** Offset of the '?' starting the query, or `omitted` if no query. */
4924
  uint32_t search_start{omitted};
4925
4926
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
4927
  uint32_t hash_start{omitted};
4928
4929
  /**
4930
   * Validates that offsets are in ascending order and consistent.
4931
   * Useful for debugging to detect internal corruption.
4932
   * @return `true` if offsets are consistent, `false` otherwise.
4933
   */
4934
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
4935
4936
  /**
4937
   * Returns a JSON string representation of the offsets for debugging.
4938
   * @return A JSON-formatted string with all offset values.
4939
   */
4940
  [[nodiscard]] std::string to_string() const;
4941
4942
};  // struct url_components
4943
}  // namespace ada
4944
#endif
4945
/* end file include/ada/url_components.h */
4946
4947
namespace ada {
4948
4949
struct url_aggregator;
4950
4951
// namespace parser {
4952
// template <typename result_type>
4953
// result_type parse_url(std::string_view user_input,
4954
//                       const result_type* base_url = nullptr);
4955
// template <typename result_type, bool store_values>
4956
// result_type parse_url_impl(std::string_view user_input,
4957
//                            const result_type* base_url = nullptr);
4958
// }
4959
4960
/**
4961
 * @brief Represents a parsed URL with individual string components.
4962
 *
4963
 * The `url` struct stores each URL component (scheme, username, password,
4964
 * host, port, path, query, fragment) as a separate `std::string`. This
4965
 * provides flexibility but incurs more memory allocations compared to
4966
 * `url_aggregator`.
4967
 *
4968
 * **When to use `ada::url`:**
4969
 * - When you need to frequently modify individual URL components
4970
 * - When you want independent ownership of component strings
4971
 *
4972
 * **When to use `ada::url_aggregator` instead:**
4973
 * - For read-mostly operations on parsed URLs
4974
 * - When memory efficiency is important
4975
 * - When you only need string_view access to components
4976
 *
4977
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
4978
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
4979
 *
4980
 * @see url_aggregator For a more memory-efficient URL representation
4981
 * @see https://url.spec.whatwg.org/#url-representation
4982
 */
4983
struct url : url_base {
4984
278k
  url() = default;
4985
42.3k
  url(const url& u) = default;
4986
252k
  url(url&& u) noexcept = default;
4987
6.78k
  url& operator=(url&& u) noexcept = default;
4988
71.0k
  url& operator=(const url& u) = default;
4989
573k
  ~url() override = default;
4990
4991
  // Fields are ordered so that the most frequently accessed components
4992
  // tend to occupy earlier cache lines and remain close together in memory.
4993
  //
4994
  // Note: The exact object layout (including cache-line boundaries, byte
4995
  // offsets, and member sizes) is implementation- and platform-dependent.
4996
  // This ordering expresses an intent for better cache locality but does not
4997
  // guarantee any specific in-memory layout.
4998
4999
  /**
5000
   * @private
5001
   * A URL's host is null or a host. It is initially null.
5002
   */
5003
  std::optional<std::string> host{};
5004
5005
  /**
5006
   * @private
5007
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5008
   * strings, usually identifying a location.
5009
   */
5010
  std::string path{};
5011
5012
  /**
5013
   * @private
5014
   * A URL's query is either null or an ASCII string. It is initially null.
5015
   */
5016
  std::optional<std::string> query{};
5017
5018
  /**
5019
   * @private
5020
   * A URL's fragment is either null or an ASCII string that can be used for
5021
   * further processing on the resource the URL's other components identify. It
5022
   * is initially null.
5023
   */
5024
  std::optional<std::string> hash{};
5025
5026
  /**
5027
   * @private
5028
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5029
   * networking port. It is initially null.
5030
   */
5031
  std::optional<uint16_t> port{};
5032
5033
  /**
5034
   * @private
5035
   * A URL's username is an ASCII string identifying a username. It is initially
5036
   * the empty string.
5037
   */
5038
  std::string username{};
5039
5040
  /**
5041
   * @private
5042
   * A URL's password is an ASCII string identifying a password. It is initially
5043
   * the empty string.
5044
   */
5045
  std::string password{};
5046
5047
  /**
5048
   * Checks if the URL has an empty hostname (host is set but empty string).
5049
   * @return `true` if host exists but is empty, `false` otherwise.
5050
   */
5051
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5052
5053
  /**
5054
   * Checks if the URL has a non-default port explicitly specified.
5055
   * @return `true` if a port is present, `false` otherwise.
5056
   */
5057
  [[nodiscard]] inline bool has_port() const noexcept;
5058
5059
  /**
5060
   * Checks if the URL has a hostname (including empty hostnames).
5061
   * @return `true` if host is present, `false` otherwise.
5062
   */
5063
  [[nodiscard]] inline bool has_hostname() const noexcept;
5064
5065
  /**
5066
   * Validates whether the hostname is a valid domain according to RFC 1034.
5067
   * Checks that the domain and its labels have valid lengths (max 255 octets
5068
   * total, max 63 octets per label).
5069
   * @return `true` if the domain is valid, `false` otherwise.
5070
   */
5071
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5072
5073
  /**
5074
   * Returns a JSON string representation of this URL for debugging.
5075
   * @return A JSON-formatted string with all URL components.
5076
   */
5077
  [[nodiscard]] std::string to_string() const override;
5078
5079
  /**
5080
   * Returns the full serialized URL (the href).
5081
   * @return The complete URL string (allocates a new string).
5082
   * @see https://url.spec.whatwg.org/#dom-url-href
5083
   */
5084
  [[nodiscard]] ada_really_inline std::string get_href() const;
5085
5086
  /**
5087
   * Returns the byte length of the serialized URL without allocating a string.
5088
   * @return Size of the href in bytes.
5089
   */
5090
  [[nodiscard]] size_t get_href_size() const noexcept;
5091
5092
  /**
5093
   * Returns the URL's origin as a string (scheme + host + port for special
5094
   * URLs).
5095
   * @return A newly allocated string containing the serialized origin.
5096
   * @see https://url.spec.whatwg.org/#concept-url-origin
5097
   */
5098
  [[nodiscard]] std::string get_origin() const override;
5099
5100
  /**
5101
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5102
   * @return A newly allocated string with the protocol.
5103
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5104
   */
5105
  [[nodiscard]] std::string get_protocol() const;
5106
5107
  /**
5108
   * Returns the URL's host and port (e.g., "example.com:8080").
5109
   * If no port is set, returns just the host. Returns empty string if no host.
5110
   * @return A newly allocated string with host:port.
5111
   * @see https://url.spec.whatwg.org/#dom-url-host
5112
   */
5113
  [[nodiscard]] std::string get_host() const;
5114
5115
  /**
5116
   * Returns the URL's hostname (without port).
5117
   * Returns empty string if no host is set.
5118
   * @return A newly allocated string with the hostname.
5119
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5120
   */
5121
  [[nodiscard]] std::string get_hostname() const;
5122
5123
  /**
5124
   * Returns the URL's path component.
5125
   * @return A string_view pointing to the path.
5126
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5127
   */
5128
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5129
5130
  /**
5131
   * Returns the byte length of the pathname without creating a string.
5132
   * @return Size of the pathname in bytes.
5133
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5134
   */
5135
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5136
5137
  /**
5138
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5139
   * Returns empty string if no query is set.
5140
   * @return A newly allocated string with the search/query.
5141
   * @see https://url.spec.whatwg.org/#dom-url-search
5142
   */
5143
  [[nodiscard]] std::string get_search() const;
5144
5145
  /**
5146
   * Returns the URL's username component.
5147
   * @return A constant reference to the username string.
5148
   * @see https://url.spec.whatwg.org/#dom-url-username
5149
   */
5150
  [[nodiscard]] const std::string& get_username() const noexcept;
5151
5152
  /**
5153
   * Sets the URL's username, percent-encoding special characters.
5154
   * @param input The new username value.
5155
   * @return `true` on success, `false` if the URL cannot have credentials.
5156
   * @see https://url.spec.whatwg.org/#dom-url-username
5157
   */
5158
  bool set_username(std::string_view input);
5159
5160
  /**
5161
   * Sets the URL's password, percent-encoding special characters.
5162
   * @param input The new password value.
5163
   * @return `true` on success, `false` if the URL cannot have credentials.
5164
   * @see https://url.spec.whatwg.org/#dom-url-password
5165
   */
5166
  bool set_password(std::string_view input);
5167
5168
  /**
5169
   * Sets the URL's port from a string (e.g., "8080").
5170
   * @param input The port string. Empty string removes the port.
5171
   * @return `true` on success, `false` if the URL cannot have a port.
5172
   * @see https://url.spec.whatwg.org/#dom-url-port
5173
   */
5174
  bool set_port(std::string_view input);
5175
5176
  /**
5177
   * Sets the URL's fragment/hash (the part after '#').
5178
   * @param input The new hash value (with or without leading '#').
5179
   * @see https://url.spec.whatwg.org/#dom-url-hash
5180
   */
5181
  void set_hash(std::string_view input);
5182
5183
  /**
5184
   * Sets the URL's query string (the part after '?').
5185
   * @param input The new query value (with or without leading '?').
5186
   * @see https://url.spec.whatwg.org/#dom-url-search
5187
   */
5188
  void set_search(std::string_view input);
5189
5190
  /**
5191
   * Sets the URL's pathname.
5192
   * @param input The new path value.
5193
   * @return `true` on success, `false` if the URL has an opaque path.
5194
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5195
   */
5196
  bool set_pathname(std::string_view input);
5197
5198
  /**
5199
   * Sets the URL's host (hostname and optionally port).
5200
   * @param input The new host value (e.g., "example.com:8080").
5201
   * @return `true` on success, `false` if parsing fails.
5202
   * @see https://url.spec.whatwg.org/#dom-url-host
5203
   */
5204
  bool set_host(std::string_view input);
5205
5206
  /**
5207
   * Sets the URL's hostname (without port).
5208
   * @param input The new hostname value.
5209
   * @return `true` on success, `false` if parsing fails.
5210
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5211
   */
5212
  bool set_hostname(std::string_view input);
5213
5214
  /**
5215
   * Sets the URL's protocol/scheme.
5216
   * @param input The new protocol (with or without trailing ':').
5217
   * @return `true` on success, `false` if the scheme is invalid.
5218
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5219
   */
5220
  bool set_protocol(std::string_view input);
5221
5222
  /**
5223
   * Replaces the entire URL by parsing a new href string.
5224
   * @param input The new URL string to parse.
5225
   * @return `true` on success, `false` if parsing fails.
5226
   * @see https://url.spec.whatwg.org/#dom-url-href
5227
   */
5228
  bool set_href(std::string_view input);
5229
5230
  /**
5231
   * Returns the URL's password component.
5232
   * @return A constant reference to the password string.
5233
   * @see https://url.spec.whatwg.org/#dom-url-password
5234
   */
5235
  [[nodiscard]] const std::string& get_password() const noexcept;
5236
5237
  /**
5238
   * Returns the URL's port as a string (e.g., "8080").
5239
   * Returns empty string if no port is set.
5240
   * @return A newly allocated string with the port.
5241
   * @see https://url.spec.whatwg.org/#dom-url-port
5242
   */
5243
  [[nodiscard]] std::string get_port() const;
5244
5245
  /**
5246
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5247
   * Returns empty string if no fragment is set.
5248
   * @return A newly allocated string with the hash.
5249
   * @see https://url.spec.whatwg.org/#dom-url-hash
5250
   */
5251
  [[nodiscard]] std::string get_hash() const;
5252
5253
  /**
5254
   * Checks if the URL has credentials (non-empty username or password).
5255
   * @return `true` if username or password is non-empty, `false` otherwise.
5256
   */
5257
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5258
5259
  /**
5260
   * Returns the URL component offsets for efficient serialization.
5261
   *
5262
   * The components represent byte offsets into the serialized URL:
5263
   * ```
5264
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5265
   *       |     |    |          | ^^^^|       |   |
5266
   *       |     |    |          | |   |       |   `----- hash_start
5267
   *       |     |    |          | |   |       `--------- search_start
5268
   *       |     |    |          | |   `----------------- pathname_start
5269
   *       |     |    |          | `--------------------- port
5270
   *       |     |    |          `----------------------- host_end
5271
   *       |     |    `---------------------------------- host_start
5272
   *       |     `--------------------------------------- username_end
5273
   *       `--------------------------------------------- protocol_end
5274
   * ```
5275
   * @return A newly constructed url_components struct.
5276
   * @see https://github.com/servo/rust-url
5277
   */
5278
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5279
5280
  /**
5281
   * Checks if the URL has a fragment/hash component.
5282
   * @return `true` if hash is present, `false` otherwise.
5283
   */
5284
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5285
5286
  /**
5287
   * Checks if the URL has a query/search component.
5288
   * @return `true` if query is present, `false` otherwise.
5289
   */
5290
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5291
5292
 private:
5293
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5294
                                                   const ada::url*);
5295
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5296
      std::string_view, const ada::url_aggregator*);
5297
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5298
      ada::url& url);
5299
5300
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5301
                                                              const ada::url*);
5302
  friend ada::url_aggregator ada::parser::parse_url_impl<
5303
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5304
  friend ada::url_aggregator ada::parser::parse_url_impl<
5305
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5306
  template <class result_type>
5307
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5308
                                                     result_type&);
5309
5310
  inline void update_unencoded_base_hash(std::string_view input);
5311
  inline void update_base_hostname(std::string_view input);
5312
  inline void update_base_search(std::string_view input,
5313
                                 const uint8_t query_percent_encode_set[]);
5314
  inline void update_base_search(std::optional<std::string>&& input);
5315
  inline void update_base_pathname(std::string_view input);
5316
  inline void update_base_username(std::string_view input);
5317
  inline void update_base_password(std::string_view input);
5318
  inline void update_base_port(std::optional<uint16_t> input);
5319
5320
  /**
5321
   * Sets the host or hostname according to override condition.
5322
   * Return true on success.
5323
   * @see https://url.spec.whatwg.org/#hostname-state
5324
   */
5325
  template <bool override_hostname = false>
5326
  bool set_host_or_hostname(std::string_view input);
5327
5328
  /**
5329
   * Return true on success.
5330
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5331
   */
5332
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5333
5334
  /**
5335
   * Return true on success.
5336
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5337
   */
5338
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5339
5340
  /**
5341
   * Return true on success.
5342
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5343
   */
5344
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5345
5346
  /**
5347
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5348
   * be used to dispatch a URL for further processing after parsing. It is
5349
   * initially the empty string. We only set non_special_scheme when the scheme
5350
   * is non-special, otherwise we avoid constructing string.
5351
   *
5352
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5353
   * typically do not need to store them in each url instance.
5354
   */
5355
  std::string non_special_scheme{};
5356
5357
  /**
5358
   * A URL cannot have a username/password/port if its host is null or the empty
5359
   * string, or its scheme is "file".
5360
   */
5361
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5362
5363
  ada_really_inline size_t parse_port(
5364
      std::string_view view, bool check_trailing_content) noexcept override;
5365
5366
2.29k
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5367
2.29k
    return this->parse_port(view, false);
5368
2.29k
  }
5369
5370
  /**
5371
   * Parse the host from the provided input. We assume that
5372
   * the input does not contain spaces or tabs. Control
5373
   * characters and spaces are not trimmed (they should have
5374
   * been removed if needed).
5375
   * Return true on success.
5376
   * @see https://url.spec.whatwg.org/#host-parsing
5377
   */
5378
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5379
5380
  template <bool has_state_override = false>
5381
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5382
5383
  constexpr void clear_pathname() override;
5384
  constexpr void clear_search() override;
5385
  constexpr void set_protocol_as_file();
5386
5387
  /**
5388
   * Parse the path from the provided input.
5389
   * Return true on success. Control characters not
5390
   * trimmed from the ends (they should have
5391
   * been removed if needed).
5392
   *
5393
   * The input is expected to be UTF-8.
5394
   *
5395
   * @see https://url.spec.whatwg.org/
5396
   */
5397
  ada_really_inline void parse_path(std::string_view input);
5398
5399
  /**
5400
   * Set the scheme for this URL. The provided scheme should be a valid
5401
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5402
   * have no spurious trailing or leading content.
5403
   */
5404
  inline void set_scheme(std::string&& new_scheme) noexcept;
5405
5406
  /**
5407
   * Take the scheme from another URL. The scheme string is moved from the
5408
   * provided url.
5409
   */
5410
  constexpr void copy_scheme(ada::url&& u);
5411
5412
  /**
5413
   * Take the scheme from another URL. The scheme string is copied from the
5414
   * provided url.
5415
   */
5416
  constexpr void copy_scheme(const ada::url& u);
5417
5418
};  // struct url
5419
5420
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5421
}  // namespace ada
5422
5423
#endif  // ADA_URL_H
5424
/* end file include/ada/url.h */
5425
5426
namespace ada {
5427
5428
/**
5429
 * Result type for URL parsing operations.
5430
 *
5431
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5432
 * error. This allows for exception-free error handling.
5433
 *
5434
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5435
 *
5436
 * @example
5437
 * ```cpp
5438
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5439
 * if (result) {
5440
 *     // Success: use result.value() or *result
5441
 * } else {
5442
 *     // Error: handle result.error()
5443
 * }
5444
 * ```
5445
 */
5446
template <class result_type = ada::url_aggregator>
5447
using result = tl::expected<result_type, ada::errors>;
5448
5449
/**
5450
 * Parses a URL string according to the WHATWG URL Standard.
5451
 *
5452
 * This is the main entry point for URL parsing in Ada. The function takes
5453
 * a string input and optionally a base URL for resolving relative URLs.
5454
 *
5455
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5456
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5457
 *         memory-efficient as it stores components as offsets into a single
5458
 *         buffer.
5459
 *
5460
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5461
 *        Leading and trailing whitespace is automatically trimmed.
5462
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5463
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5464
 *
5465
 * @return A `result<result_type>` containing either the parsed URL on success,
5466
 *         or an error code on failure. Use the boolean conversion or
5467
 *         `has_value()` to check for success.
5468
 *
5469
 * @note The parser is fully compliant with the WHATWG URL Standard.
5470
 *
5471
 * Parsing fails if the input or the resulting normalized URL exceeds
5472
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5473
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5474
 * a short input that normalizes into a long URL is still rejected.
5475
 *
5476
 * @example
5477
 * ```cpp
5478
 * // Parse an absolute URL
5479
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5480
 * if (url) {
5481
 *     std::cout << url->get_hostname(); // "example.com"
5482
 *     std::cout << url->get_pathname(); // "/path"
5483
 * }
5484
 *
5485
 * // Parse a relative URL with a base
5486
 * auto base = ada::parse("https://example.com/dir/");
5487
 * if (base) {
5488
 *     auto relative = ada::parse("../other/page", &*base);
5489
 *     if (relative) {
5490
 *         std::cout << relative->get_href(); //
5491
 * "https://example.com/other/page"
5492
 *     }
5493
 * }
5494
 * ```
5495
 *
5496
 * @see https://url.spec.whatwg.org/#url-parsing
5497
 */
5498
template <class result_type = ada::url_aggregator>
5499
ada_warn_unused ada::result<result_type> parse(
5500
    std::string_view input, const result_type* base_url = nullptr);
5501
5502
extern template ada::result<url> parse<url>(std::string_view input,
5503
                                            const url* base_url);
5504
extern template ada::result<url_aggregator> parse<url_aggregator>(
5505
    std::string_view input, const url_aggregator* base_url);
5506
5507
/**
5508
 * Checks whether a URL string can be successfully parsed.
5509
 *
5510
 * Equivalent to `parse(input, base).has_value()` for every input, including
5511
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5512
 *
5513
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5514
 * @param base_input Optional base URL string for relative inputs.
5515
 * @return `true` if parsing would succeed, `false` otherwise.
5516
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5517
 */
5518
bool can_parse(std::string_view input,
5519
               const std::string_view* base_input = nullptr);
5520
5521
#if ADA_INCLUDE_URL_PATTERN
5522
/**
5523
 * Parses a URL pattern according to the URLPattern specification.
5524
 *
5525
 * URL patterns provide a syntax for matching URLs against patterns, similar
5526
 * to how regular expressions match strings. This is useful for routing and
5527
 * URL-based dispatching.
5528
 *
5529
 * @tparam regex_provider The regex implementation to use for pattern matching.
5530
 *
5531
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5532
 *        struct specifying individual component patterns.
5533
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5534
 *        resolving relative patterns.
5535
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5536
 *
5537
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5538
 *         or an error code on failure.
5539
 *
5540
 * @see https://urlpattern.spec.whatwg.org
5541
 */
5542
template <url_pattern_regex::regex_concept regex_provider>
5543
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5544
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5545
                  const std::string_view* base_url = nullptr,
5546
                  const url_pattern_options* options = nullptr);
5547
#endif  // ADA_INCLUDE_URL_PATTERN
5548
5549
/**
5550
 * Converts a file system path to a file:// URL.
5551
 *
5552
 * Creates a properly formatted file URL from a local file system path.
5553
 * Handles platform-specific path separators and percent-encoding.
5554
 *
5555
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5556
 *
5557
 * @return A file:// URL string representing the given path.
5558
 */
5559
std::string href_from_file(std::string_view path);
5560
5561
/**
5562
 * Sets the maximum allowed length for URLs.
5563
 *
5564
 * Both the raw input and the resulting normalized URL (the href) are checked
5565
 * against this limit. Parsing or setter calls that would produce a URL
5566
 * exceeding this length are rejected. The value must fit in a uint32_t.
5567
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5568
 *
5569
 * @param length The new maximum URL length in bytes.
5570
 */
5571
void set_max_input_length(uint32_t length);
5572
5573
/**
5574
 * Returns the current maximum allowed length for URLs.
5575
 *
5576
 * @return The current maximum URL length in bytes.
5577
 */
5578
uint32_t get_max_input_length();
5579
5580
}  // namespace ada
5581
5582
#endif  // ADA_IMPLEMENTATION_H
5583
/* end file include/ada/implementation.h */
5584
5585
#include <ostream>
5586
#include <string>
5587
#include <string_view>
5588
#include <unordered_map>
5589
#include <variant>
5590
#include <vector>
5591
5592
#if ADA_TESTING
5593
#include <iostream>
5594
#endif  // ADA_TESTING
5595
5596
#if ADA_INCLUDE_URL_PATTERN
5597
namespace ada {
5598
5599
enum class url_pattern_part_type : uint8_t {
5600
  // The part represents a simple fixed text string.
5601
  FIXED_TEXT,
5602
  // The part represents a matching group with a custom regular expression.
5603
  REGEXP,
5604
  // The part represents a matching group that matches code points up to the
5605
  // next separator code point. This is typically used for a named group like
5606
  // ":foo" that does not have a custom regular expression.
5607
  SEGMENT_WILDCARD,
5608
  // The part represents a matching group that greedily matches all code points.
5609
  // This is typically used for the "*" wildcard matching group.
5610
  FULL_WILDCARD,
5611
};
5612
5613
// Pattern type for fast-path matching optimization.
5614
// This allows skipping expensive regex evaluation for common simple patterns.
5615
enum class url_pattern_component_type : uint8_t {
5616
  // Pattern is "^$" - only matches empty string
5617
  EMPTY,
5618
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5619
  EXACT_MATCH,
5620
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5621
  FULL_WILDCARD,
5622
  // Pattern requires actual regex evaluation
5623
  REGEXP,
5624
};
5625
5626
enum class url_pattern_part_modifier : uint8_t {
5627
  // The part does not have a modifier.
5628
  none,
5629
  // The part has an optional modifier indicated by the U+003F (?) code point.
5630
  optional,
5631
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5632
  // point.
5633
  zero_or_more,
5634
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5635
  // point.
5636
  one_or_more,
5637
};
5638
5639
// @see https://urlpattern.spec.whatwg.org/#part
5640
class url_pattern_part {
5641
 public:
5642
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5643
                   url_pattern_part_modifier _modifier)
5644
219k
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5645
5646
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5647
                   url_pattern_part_modifier _modifier, std::string&& _name,
5648
                   std::string&& _prefix, std::string&& _suffix)
5649
493k
      : type(_type),
5650
493k
        value(std::move(_value)),
5651
493k
        modifier(_modifier),
5652
493k
        name(std::move(_name)),
5653
493k
        prefix(std::move(_prefix)),
5654
493k
        suffix(std::move(_suffix)) {}
5655
  // A part has an associated type, a string, which must be set upon creation.
5656
  url_pattern_part_type type;
5657
  // A part has an associated value, a string, which must be set upon creation.
5658
  std::string value;
5659
  // A part has an associated modifier a string, which must be set upon
5660
  // creation.
5661
  url_pattern_part_modifier modifier;
5662
  // A part has an associated name, a string, initially the empty string.
5663
  std::string name{};
5664
  // A part has an associated prefix, a string, initially the empty string.
5665
  std::string prefix{};
5666
  // A part has an associated suffix, a string, initially the empty string.
5667
  std::string suffix{};
5668
5669
  inline bool is_regexp() const noexcept;
5670
};
5671
5672
// @see https://urlpattern.spec.whatwg.org/#options-header
5673
struct url_pattern_compile_component_options {
5674
  url_pattern_compile_component_options() = default;
5675
  explicit url_pattern_compile_component_options(
5676
      std::optional<char> new_delimiter = std::nullopt,
5677
      std::optional<char> new_prefix = std::nullopt) noexcept
5678
45
      : delimiter(new_delimiter), prefix(new_prefix) {}
5679
5680
  inline std::string_view get_delimiter() const ada_warn_unused;
5681
  inline std::string_view get_prefix() const ada_warn_unused;
5682
5683
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5684
  bool ignore_case = false;
5685
5686
  static url_pattern_compile_component_options DEFAULT;
5687
  static url_pattern_compile_component_options HOSTNAME;
5688
  static url_pattern_compile_component_options PATHNAME;
5689
5690
 private:
5691
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5692
  std::optional<char> delimiter{};
5693
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5694
  std::optional<char> prefix{};
5695
};
5696
5697
// The default options is an options struct with delimiter code point set to
5698
// the empty string and prefix code point set to the empty string.
5699
inline url_pattern_compile_component_options
5700
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5701
5702
// The hostname options is an options struct with delimiter code point set
5703
// "." and prefix code point set to the empty string.
5704
inline url_pattern_compile_component_options
5705
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5706
5707
// The pathname options is an options struct with delimiter code point set
5708
// "/" and prefix code point set to "/".
5709
inline url_pattern_compile_component_options
5710
    url_pattern_compile_component_options::PATHNAME('/', '/');
5711
5712
// A struct providing the URLPattern matching results for a single
5713
// URL component. The URLPatternComponentResult is only ever used
5714
// as a member attribute of a URLPatternResult struct. The
5715
// URLPatternComponentResult API is defined as part of the URLPattern
5716
// specification.
5717
struct url_pattern_component_result {
5718
  std::string input;
5719
  std::unordered_map<std::string, std::optional<std::string>> groups;
5720
5721
  bool operator==(const url_pattern_component_result&) const;
5722
5723
#if ADA_TESTING
5724
  friend void PrintTo(const url_pattern_component_result& result,
5725
                      std::ostream* os) {
5726
    *os << "input: '" << result.input << "', group: ";
5727
    for (const auto& group : result.groups) {
5728
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5729
          << ") ";
5730
    }
5731
  }
5732
#endif  // ADA_TESTING
5733
};
5734
5735
template <url_pattern_regex::regex_concept regex_provider>
5736
class url_pattern_component {
5737
 public:
5738
905k
  url_pattern_component() = default;
5739
5740
  // This function explicitly takes a std::string because it is moved.
5741
  // To avoid unnecessary copy, move each value while calling the constructor.
5742
  url_pattern_component(std::string&& new_pattern,
5743
                        typename regex_provider::regex_type&& new_regexp,
5744
                        std::vector<std::string>&& new_group_name_list,
5745
                        bool new_has_regexp_groups,
5746
                        url_pattern_component_type new_type,
5747
                        std::string&& new_exact_match_value = {})
5748
916k
      : regexp(std::move(new_regexp)),
5749
916k
        pattern(std::move(new_pattern)),
5750
916k
        group_name_list(std::move(new_group_name_list)),
5751
916k
        exact_match_value(std::move(new_exact_match_value)),
5752
916k
        has_regexp_groups(new_has_regexp_groups),
5753
916k
        type(new_type) {}
5754
5755
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5756
  template <url_pattern_encoding_callback F>
5757
  static tl::expected<url_pattern_component, errors> compile(
5758
      std::string_view input, F& encoding_callback,
5759
      url_pattern_compile_component_options& options);
5760
5761
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5762
  url_pattern_component_result create_component_match_result(
5763
      std::string&& input,
5764
      std::vector<std::optional<std::string>>&& exec_result);
5765
5766
  // Fast path test that returns true/false without constructing result groups.
5767
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5768
  bool fast_test(std::string_view input) const noexcept;
5769
5770
  // Fast path match that returns capture groups without regex for simple
5771
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5772
  // capture groups.
5773
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5774
      std::string_view input) const;
5775
5776
#if ADA_TESTING
5777
  friend void PrintTo(const url_pattern_component& component,
5778
                      std::ostream* os) {
5779
    *os << "pattern: '" << component.pattern
5780
        << "', has_regexp_groups: " << component.has_regexp_groups
5781
        << "group_name_list: ";
5782
    for (const auto& name : component.group_name_list) {
5783
      *os << name << ", ";
5784
    }
5785
  }
5786
#endif  // ADA_TESTING
5787
5788
  typename regex_provider::regex_type regexp{};
5789
  std::string pattern{};
5790
  std::vector<std::string> group_name_list{};
5791
  // For EXACT_MATCH type: the literal string to compare against
5792
  std::string exact_match_value{};
5793
  bool has_regexp_groups = false;
5794
  // Cached pattern type for fast-path optimization
5795
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5796
};
5797
5798
// A URLPattern input can be either a string or a URLPatternInit object.
5799
// If it is a string, it must be a valid UTF-8 string.
5800
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5801
5802
// A struct providing the URLPattern matching results for all
5803
// components of a URL. The URLPatternResult API is defined as
5804
// part of the URLPattern specification.
5805
struct url_pattern_result {
5806
  std::vector<url_pattern_input> inputs;
5807
  url_pattern_component_result protocol;
5808
  url_pattern_component_result username;
5809
  url_pattern_component_result password;
5810
  url_pattern_component_result hostname;
5811
  url_pattern_component_result port;
5812
  url_pattern_component_result pathname;
5813
  url_pattern_component_result search;
5814
  url_pattern_component_result hash;
5815
};
5816
5817
struct url_pattern_options {
5818
  bool ignore_case = false;
5819
5820
#if ADA_TESTING
5821
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
5822
    *os << "ignore_case: '" << options.ignore_case;
5823
  }
5824
#endif  // ADA_TESTING
5825
};
5826
5827
/**
5828
 * @brief URL pattern matching class implementing the URLPattern API.
5829
 *
5830
 * URLPattern provides a way to match URLs against patterns with wildcards
5831
 * and named capture groups. It's useful for routing, URL-based dispatching,
5832
 * and URL validation.
5833
 *
5834
 * Pattern syntax supports:
5835
 * - Literal text matching
5836
 * - Named groups: `:name` (matches up to the next separator)
5837
 * - Wildcards: `*` (matches everything)
5838
 * - Custom regex: `(pattern)`
5839
 * - Optional segments: `:name?`
5840
 * - Repeated segments: `:name+`, `:name*`
5841
 *
5842
 * @tparam regex_provider The regex implementation to use for pattern matching.
5843
 *         Must satisfy the url_pattern_regex::regex_concept.
5844
 *
5845
 * @note All string inputs must be valid UTF-8.
5846
 *
5847
 * @see https://urlpattern.spec.whatwg.org/
5848
 */
5849
template <url_pattern_regex::regex_concept regex_provider>
5850
class url_pattern {
5851
 public:
5852
113k
  url_pattern() = default;
5853
5854
  /**
5855
   * If non-null, base_url must pointer at a valid UTF-8 string.
5856
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
5857
   */
5858
  result<std::optional<url_pattern_result>> exec(
5859
      const url_pattern_input& input,
5860
      const std::string_view* base_url = nullptr);
5861
5862
  /**
5863
   * If non-null, base_url must pointer at a valid UTF-8 string.
5864
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
5865
   */
5866
  result<bool> test(const url_pattern_input& input,
5867
                    const std::string_view* base_url = nullptr);
5868
5869
  /**
5870
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
5871
   * This function expects a valid UTF-8 string if input is a string.
5872
   */
5873
  result<std::optional<url_pattern_result>> match(
5874
      const url_pattern_input& input,
5875
      const std::string_view* base_url_string = nullptr);
5876
5877
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
5878
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
5879
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
5880
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
5881
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
5882
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
5883
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
5884
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
5885
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
5886
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
5887
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
5888
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
5889
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
5890
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
5891
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
5892
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
5893
5894
  // If ignoreCase is true, the JavaScript regular expression created for each
5895
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
5896
  [[nodiscard]] bool ignore_case() const;
5897
5898
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
5899
  [[nodiscard]] bool has_regexp_groups() const;
5900
5901
  // Helper to test all components at once. Returns true if all match.
5902
  [[nodiscard]] bool test_components(
5903
      std::string_view protocol, std::string_view username,
5904
      std::string_view password, std::string_view hostname,
5905
      std::string_view port, std::string_view pathname, std::string_view search,
5906
      std::string_view hash) const;
5907
5908
#if ADA_TESTING
5909
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
5910
    *os << "protocol_component: '" << c.get_protocol() << ", ";
5911
    *os << "username_component: '" << c.get_username() << ", ";
5912
    *os << "password_component: '" << c.get_password() << ", ";
5913
    *os << "hostname_component: '" << c.get_hostname() << ", ";
5914
    *os << "port_component: '" << c.get_port() << ", ";
5915
    *os << "pathname_component: '" << c.get_pathname() << ", ";
5916
    *os << "search_component: '" << c.get_search() << ", ";
5917
    *os << "hash_component: '" << c.get_hash();
5918
  }
5919
#endif  // ADA_TESTING
5920
5921
  template <url_pattern_regex::regex_concept P>
5922
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
5923
      std::variant<std::string_view, url_pattern_init>&& input,
5924
      const std::string_view* base_url, const url_pattern_options* options);
5925
5926
  /**
5927
   * @private
5928
   * We can not make this private due to a LLVM bug.
5929
   * Ref: https://github.com/ada-url/ada/pull/859
5930
   */
5931
  url_pattern_component<regex_provider> protocol_component{};
5932
  /**
5933
   * @private
5934
   * We can not make this private due to a LLVM bug.
5935
   * Ref: https://github.com/ada-url/ada/pull/859
5936
   */
5937
  url_pattern_component<regex_provider> username_component{};
5938
  /**
5939
   * @private
5940
   * We can not make this private due to a LLVM bug.
5941
   * Ref: https://github.com/ada-url/ada/pull/859
5942
   */
5943
  url_pattern_component<regex_provider> password_component{};
5944
  /**
5945
   * @private
5946
   * We can not make this private due to a LLVM bug.
5947
   * Ref: https://github.com/ada-url/ada/pull/859
5948
   */
5949
  url_pattern_component<regex_provider> hostname_component{};
5950
  /**
5951
   * @private
5952
   * We can not make this private due to a LLVM bug.
5953
   * Ref: https://github.com/ada-url/ada/pull/859
5954
   */
5955
  url_pattern_component<regex_provider> port_component{};
5956
  /**
5957
   * @private
5958
   * We can not make this private due to a LLVM bug.
5959
   * Ref: https://github.com/ada-url/ada/pull/859
5960
   */
5961
  url_pattern_component<regex_provider> pathname_component{};
5962
  /**
5963
   * @private
5964
   * We can not make this private due to a LLVM bug.
5965
   * Ref: https://github.com/ada-url/ada/pull/859
5966
   */
5967
  url_pattern_component<regex_provider> search_component{};
5968
  /**
5969
   * @private
5970
   * We can not make this private due to a LLVM bug.
5971
   * Ref: https://github.com/ada-url/ada/pull/859
5972
   */
5973
  url_pattern_component<regex_provider> hash_component{};
5974
  /**
5975
   * @private
5976
   * We can not make this private due to a LLVM bug.
5977
   * Ref: https://github.com/ada-url/ada/pull/859
5978
   */
5979
  bool ignore_case_ = false;
5980
};
5981
}  // namespace ada
5982
#endif  // ADA_INCLUDE_URL_PATTERN
5983
#endif
5984
/* end file include/ada/url_pattern.h */
5985
/* begin file include/ada/url_pattern_helpers.h */
5986
/**
5987
 * @file url_pattern_helpers.h
5988
 * @brief Declaration for the URLPattern helpers.
5989
 */
5990
#ifndef ADA_URL_PATTERN_HELPERS_H
5991
#define ADA_URL_PATTERN_HELPERS_H
5992
5993
5994
#include <string>
5995
#include <tuple>
5996
#include <vector>
5997
5998
#if ADA_INCLUDE_URL_PATTERN
5999
namespace ada {
6000
enum class errors : uint8_t;
6001
}
6002
6003
namespace ada::url_pattern_helpers {
6004
6005
// @see https://urlpattern.spec.whatwg.org/#token
6006
enum class token_type : uint8_t {
6007
  INVALID_CHAR,    // 0
6008
  OPEN,            // 1
6009
  CLOSE,           // 2
6010
  REGEXP,          // 3
6011
  NAME,            // 4
6012
  CHAR,            // 5
6013
  ESCAPED_CHAR,    // 6
6014
  OTHER_MODIFIER,  // 7
6015
  ASTERISK,        // 8
6016
  END,             // 9
6017
};
6018
6019
#ifdef ADA_TESTING
6020
std::string to_string(token_type type);
6021
#endif  // ADA_TESTING
6022
6023
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6024
enum class token_policy {
6025
  strict,
6026
  lenient,
6027
};
6028
6029
// @see https://urlpattern.spec.whatwg.org/#tokens
6030
class token {
6031
 public:
6032
  token(token_type _type, size_t _index, std::string_view _value)
6033
3.75M
      : type(_type), index(_index), value(_value) {}
6034
6035
  // A token has an associated type, a string, initially "invalid-char".
6036
  token_type type = token_type::INVALID_CHAR;
6037
6038
  // A token has an associated index, a number, initially 0. It is the position
6039
  // of the first code point in the pattern string represented by the token.
6040
  size_t index = 0;
6041
6042
  // A token has an associated value, a string, initially the empty string. It
6043
  // contains the code points from the pattern string represented by the token.
6044
  std::string_view value{};
6045
};
6046
6047
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6048
template <url_pattern_encoding_callback F>
6049
class url_pattern_parser {
6050
 public:
6051
  url_pattern_parser(F& encoding_callback_,
6052
                     std::string_view segment_wildcard_regexp_)
6053
922k
      : encoding_callback(encoding_callback_),
6054
922k
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6055
6056
3.62M
  bool can_continue() const { return index < tokens.size(); }
6057
6058
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6059
  token* try_consume_token(token_type type);
6060
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6061
  token* try_consume_modifier_token();
6062
  // @see
6063
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6064
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6065
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6066
  std::string consume_text();
6067
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6068
  bool consume_required_token(token_type type);
6069
  // @see
6070
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6071
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6072
      ada_warn_unused;
6073
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6074
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6075
                                 token* regexp_or_wildcard_token,
6076
                                 std::string_view suyffix,
6077
                                 token* modifier_token) ada_warn_unused;
6078
6079
  std::vector<token> tokens{};
6080
  F& encoding_callback;
6081
  std::string segment_wildcard_regexp;
6082
  std::vector<url_pattern_part> parts{};
6083
  std::string pending_fixed_value{};
6084
  size_t index = 0;
6085
  size_t next_numeric_name = 0;
6086
};
6087
6088
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6089
class Tokenizer {
6090
 public:
6091
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6092
997k
      : input(new_input), policy(new_policy) {}
6093
6094
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6095
  constexpr void get_next_code_point();
6096
6097
  // True when the most recent decoded unit was malformed UTF-8.
6098
2.83M
  bool had_invalid_code_point() const { return invalid_code_point; }
6099
6100
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6101
  constexpr void seek_and_get_next_code_point(size_t index);
6102
6103
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6104
6105
  void add_token(token_type type, size_t next_position, size_t value_position,
6106
                 size_t value_length);
6107
6108
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6109
  void add_token_with_default_length(token_type type, size_t next_position,
6110
                                     size_t value_position);
6111
6112
  // @see
6113
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6114
  void add_token_with_defaults(token_type type);
6115
6116
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6117
  std::optional<errors> process_tokenizing_error(
6118
      size_t next_position, size_t value_position) ada_warn_unused;
6119
6120
  friend tl::expected<std::vector<token>, errors> tokenize(
6121
      std::string_view input, token_policy policy);
6122
6123
 private:
6124
  // has an associated input, a pattern string, initially the empty string.
6125
  std::string_view input;
6126
  // has an associated policy, a tokenize policy, initially "strict".
6127
  token_policy policy;
6128
  // has an associated token list, a token list, initially an empty list.
6129
  std::vector<token> token_list{};
6130
  // has an associated index, a number, initially 0.
6131
  size_t index = 0;
6132
  // has an associated next index, a number, initially 0.
6133
  size_t next_index = 0;
6134
  // has an associated code point, a Unicode code point, initially null.
6135
  char32_t code_point{};
6136
  // Tracks whether the last decoded code point was malformed UTF-8.
6137
  bool invalid_code_point = false;
6138
};
6139
6140
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6141
template <url_pattern_regex::regex_concept regex_provider>
6142
struct constructor_string_parser {
6143
  explicit constructor_string_parser(std::string_view new_input,
6144
                                     std::vector<token>&& new_token_list)
6145
74.8k
      : input(new_input), token_list(std::move(new_token_list)) {}
6146
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6147
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6148
6149
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6150
  enum class State {
6151
    INIT,
6152
    PROTOCOL,
6153
    AUTHORITY,
6154
    USERNAME,
6155
    PASSWORD,
6156
    HOSTNAME,
6157
    PORT,
6158
    PATHNAME,
6159
    SEARCH,
6160
    HASH,
6161
    DONE,
6162
  };
6163
6164
  // @see
6165
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6166
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6167
6168
 private:
6169
  // @see https://urlpattern.spec.whatwg.org/#rewind
6170
  constexpr void rewind();
6171
6172
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6173
  constexpr bool is_hash_prefix();
6174
6175
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6176
  constexpr bool is_search_prefix();
6177
6178
  // @see https://urlpattern.spec.whatwg.org/#change-state
6179
  void change_state(State state, size_t skip);
6180
6181
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6182
  constexpr bool is_group_open() const;
6183
6184
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6185
  constexpr bool is_group_close() const;
6186
6187
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6188
  constexpr bool is_protocol_suffix() const;
6189
6190
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6191
  constexpr bool next_is_authority_slashes() const;
6192
6193
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6194
  constexpr bool is_an_identity_terminator() const;
6195
6196
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6197
  constexpr bool is_pathname_start() const;
6198
6199
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6200
  constexpr bool is_password_prefix() const;
6201
6202
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6203
  constexpr bool is_an_ipv6_open() const;
6204
6205
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6206
  constexpr bool is_an_ipv6_close() const;
6207
6208
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6209
  constexpr bool is_port_prefix() const;
6210
6211
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6212
  constexpr bool is_non_special_pattern_char(size_t index,
6213
                                             uint32_t value) const;
6214
6215
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6216
  constexpr const token* get_safe_token(size_t index) const;
6217
6218
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6219
  std::string make_component_string();
6220
  // has an associated input, a string, which must be set upon creation.
6221
  std::string_view input;
6222
  // has an associated token list, a token list, which must be set upon
6223
  // creation.
6224
  std::vector<token> token_list;
6225
  // has an associated result, a URLPatternInit, initially set to a new
6226
  // URLPatternInit.
6227
  url_pattern_init result{};
6228
  // has an associated component start, a number, initially set to 0.
6229
  size_t component_start = 0;
6230
  // has an associated token index, a number, initially set to 0.
6231
  size_t token_index = 0;
6232
  // has an associated token increment, a number, initially set to 1.
6233
  size_t token_increment = 1;
6234
  // has an associated group depth, a number, initially set to 0.
6235
  size_t group_depth = 0;
6236
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6237
  // 0.
6238
  size_t hostname_ipv6_bracket_depth = 0;
6239
  // has an associated protocol matches a special scheme flag, a boolean,
6240
  // initially set to false.
6241
  bool protocol_matches_a_special_scheme_flag = false;
6242
  // has an associated state, a string, initially set to "init".
6243
  State state = State::INIT;
6244
};
6245
6246
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6247
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6248
6249
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6250
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6251
6252
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6253
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6254
6255
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6256
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6257
6258
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6259
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6260
    std::string_view input);
6261
6262
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6263
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6264
6265
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6266
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6267
    std::string_view input, std::string_view protocol);
6268
6269
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6270
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6271
6272
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6273
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6274
    std::string_view input);
6275
6276
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6277
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6278
6279
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6280
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6281
6282
// @see https://urlpattern.spec.whatwg.org/#tokenize
6283
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6284
                                                  token_policy policy);
6285
6286
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6287
std::string process_base_url_string(std::string_view input,
6288
                                    url_pattern_init::process_type type);
6289
6290
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6291
std::string escape_pattern_string(std::string_view input);
6292
6293
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6294
std::string escape_regexp_string(std::string_view input);
6295
6296
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6297
constexpr bool is_absolute_pathname(
6298
    std::string_view input, url_pattern_init::process_type type) noexcept;
6299
6300
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6301
template <url_pattern_encoding_callback F>
6302
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6303
    std::string_view input, url_pattern_compile_component_options& options,
6304
    F& encoding_callback);
6305
6306
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6307
std::string generate_pattern_string(
6308
    std::vector<url_pattern_part>& part_list,
6309
    url_pattern_compile_component_options& options);
6310
6311
// @see
6312
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6313
std::tuple<std::string, std::vector<std::string>>
6314
generate_regular_expression_and_name_list(
6315
    const std::vector<url_pattern_part>& part_list,
6316
    url_pattern_compile_component_options options);
6317
6318
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6319
bool is_ipv6_address(std::string_view input) noexcept;
6320
6321
// @see
6322
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6323
template <url_pattern_regex::regex_concept regex_provider>
6324
bool protocol_component_matches_special_scheme(
6325
    ada::url_pattern_component<regex_provider>& input);
6326
6327
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6328
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6329
6330
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6331
std::string generate_segment_wildcard_regexp(
6332
    url_pattern_compile_component_options options);
6333
6334
}  // namespace ada::url_pattern_helpers
6335
#endif  // ADA_INCLUDE_URL_PATTERN
6336
#endif
6337
/* end file include/ada/url_pattern_helpers.h */
6338
6339
#include <string>
6340
#include <string_view>
6341
#include <variant>
6342
6343
namespace ada::parser {
6344
#if ADA_INCLUDE_URL_PATTERN
6345
template <url_pattern_regex::regex_concept regex_provider>
6346
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6347
    std::variant<std::string_view, url_pattern_init>&& input,
6348
199k
    const std::string_view* base_url, const url_pattern_options* options) {
6349
  // Let init be null.
6350
199k
  url_pattern_init init;
6351
6352
  // If input is a scalar value string then:
6353
199k
  if (std::holds_alternative<std::string_view>(input)) {
6354
    // Set init to the result of running parse a constructor string given input.
6355
74.8k
    auto parse_result =
6356
74.8k
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6357
74.8k
            std::get<std::string_view>(input));
6358
74.8k
    if (!parse_result) {
6359
1.44k
      ada_log("constructor_string_parser::parse failed");
6360
1.44k
      return tl::unexpected(parse_result.error());
6361
1.44k
    }
6362
73.3k
    init = std::move(*parse_result);
6363
    // If baseURL is null and init["protocol"] does not exist, then throw a
6364
    // TypeError.
6365
73.3k
    if (!base_url && !init.protocol) {
6366
11.9k
      ada_log("base url is null and protocol is not set");
6367
11.9k
      return tl::unexpected(errors::type_error);
6368
11.9k
    }
6369
6370
    // If baseURL is not null, set init["baseURL"] to baseURL.
6371
61.3k
    if (base_url) {
6372
48.9k
      init.base_url = std::string(*base_url);
6373
48.9k
    }
6374
124k
  } else {
6375
    // Assert: input is a URLPatternInit.
6376
124k
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6377
    // If baseURL is not null, then throw a TypeError.
6378
124k
    if (base_url) {
6379
24.9k
      ada_log("base url is not null");
6380
24.9k
      return tl::unexpected(errors::type_error);
6381
24.9k
    }
6382
    // Optimization: Avoid copy by moving the input value.
6383
    // Set init to input.
6384
99.7k
    init = std::move(std::get<url_pattern_init>(input));
6385
99.7k
  }
6386
6387
  // Let processedInit be the result of process a URLPatternInit given init,
6388
  // "pattern", null, null, null, null, null, null, null, and null.
6389
161k
  auto processed_init =
6390
161k
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6391
161k
  if (!processed_init) {
6392
47.9k
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6393
47.9k
    return tl::unexpected(processed_init.error());
6394
47.9k
  }
6395
6396
  // For each componentName of  "protocol", "username", "password", "hostname",
6397
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6398
  // not exist, then set processedInit[componentName] to "*".
6399
113k
  ADA_ASSERT_TRUE(processed_init.has_value());
6400
113k
  if (!processed_init->protocol) processed_init->protocol = "*";
6401
113k
  if (!processed_init->username) processed_init->username = "*";
6402
113k
  if (!processed_init->password) processed_init->password = "*";
6403
113k
  if (!processed_init->hostname) processed_init->hostname = "*";
6404
113k
  if (!processed_init->port) processed_init->port = "*";
6405
113k
  if (!processed_init->pathname) processed_init->pathname = "*";
6406
113k
  if (!processed_init->search) processed_init->search = "*";
6407
113k
  if (!processed_init->hash) processed_init->hash = "*";
6408
6409
113k
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6410
113k
  ada_log("-- processed_init->username: ", processed_init->username.value());
6411
113k
  ada_log("-- processed_init->password: ", processed_init->password.value());
6412
113k
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6413
113k
  ada_log("-- processed_init->port: ", processed_init->port.value());
6414
113k
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6415
113k
  ada_log("-- processed_init->search: ", processed_init->search.value());
6416
113k
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6417
6418
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6419
  // is a string which represents its corresponding default port in radix-10
6420
  // using ASCII digits then set processedInit["port"] to the empty string.
6421
  // TODO: Optimization opportunity.
6422
113k
  if (scheme::is_special(*processed_init->protocol)) {
6423
63.3k
    std::string_view port = processed_init->port.value();
6424
63.3k
    if (std::to_string(scheme::get_special_port(*processed_init->protocol)) ==
6425
63.3k
        port) {
6426
3
      processed_init->port->clear();
6427
3
    }
6428
63.3k
  }
6429
6430
  // Let urlPattern be a new URL pattern.
6431
113k
  url_pattern<regex_provider> url_pattern_{};
6432
6433
  // Set urlPattern's protocol component to the result of compiling a component
6434
  // given processedInit["protocol"], canonicalize a protocol, and default
6435
  // options.
6436
113k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6437
113k
      processed_init->protocol.value(),
6438
113k
      url_pattern_helpers::canonicalize_protocol,
6439
113k
      url_pattern_compile_component_options::DEFAULT);
6440
113k
  if (!protocol_component) {
6441
1.35k
    ada_log("url_pattern_component::compile failed for protocol ",
6442
1.35k
            processed_init->protocol.value());
6443
1.35k
    return tl::unexpected(protocol_component.error());
6444
1.35k
  }
6445
111k
  url_pattern_.protocol_component = std::move(*protocol_component);
6446
6447
  // Set urlPattern's username component to the result of compiling a component
6448
  // given processedInit["username"], canonicalize a username, and default
6449
  // options.
6450
111k
  auto username_component = url_pattern_component<regex_provider>::compile(
6451
111k
      processed_init->username.value(),
6452
111k
      url_pattern_helpers::canonicalize_username,
6453
111k
      url_pattern_compile_component_options::DEFAULT);
6454
111k
  if (!username_component) {
6455
308
    ada_log("url_pattern_component::compile failed for username ",
6456
308
            processed_init->username.value());
6457
308
    return tl::unexpected(username_component.error());
6458
308
  }
6459
111k
  url_pattern_.username_component = std::move(*username_component);
6460
6461
  // Set urlPattern's password component to the result of compiling a component
6462
  // given processedInit["password"], canonicalize a password, and default
6463
  // options.
6464
111k
  auto password_component = url_pattern_component<regex_provider>::compile(
6465
111k
      processed_init->password.value(),
6466
111k
      url_pattern_helpers::canonicalize_password,
6467
111k
      url_pattern_compile_component_options::DEFAULT);
6468
111k
  if (!password_component) {
6469
103
    ada_log("url_pattern_component::compile failed for password ",
6470
103
            processed_init->password.value());
6471
103
    return tl::unexpected(password_component.error());
6472
103
  }
6473
111k
  url_pattern_.password_component = std::move(*password_component);
6474
6475
  // TODO: Optimization opportunity. The following if statement can be
6476
  // simplified.
6477
  // If the result running hostname pattern is an IPv6 address given
6478
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6479
  // to the result of compiling a component given processedInit["hostname"],
6480
  // canonicalize an IPv6 hostname, and hostname options.
6481
111k
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6482
154
    ada_log("processed_init->hostname is ipv6 address");
6483
    // then set urlPattern's hostname component to the result of compiling a
6484
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6485
    // and hostname options.
6486
154
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6487
154
        processed_init->hostname.value(),
6488
154
        url_pattern_helpers::canonicalize_ipv6_hostname,
6489
154
        url_pattern_compile_component_options::DEFAULT);
6490
154
    if (!hostname_component) {
6491
30
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6492
30
              processed_init->hostname.value());
6493
30
      return tl::unexpected(hostname_component.error());
6494
30
    }
6495
124
    url_pattern_.hostname_component = std::move(*hostname_component);
6496
111k
  } else {
6497
    // Otherwise, set urlPattern's hostname component to the result of compiling
6498
    // a component given processedInit["hostname"], canonicalize a hostname, and
6499
    // hostname options.
6500
111k
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6501
111k
        processed_init->hostname.value(),
6502
111k
        url_pattern_helpers::canonicalize_hostname,
6503
111k
        url_pattern_compile_component_options::HOSTNAME);
6504
111k
    if (!hostname_component) {
6505
1.30k
      ada_log("url_pattern_component::compile failed for hostname ",
6506
1.30k
              processed_init->hostname.value());
6507
1.30k
      return tl::unexpected(hostname_component.error());
6508
1.30k
    }
6509
109k
    url_pattern_.hostname_component = std::move(*hostname_component);
6510
109k
  }
6511
6512
  // Set urlPattern's port component to the result of compiling a component
6513
  // given processedInit["port"], canonicalize a port, and default options.
6514
110k
  auto port_component = url_pattern_component<regex_provider>::compile(
6515
110k
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6516
110k
      url_pattern_compile_component_options::DEFAULT);
6517
110k
  if (!port_component) {
6518
410
    ada_log("url_pattern_component::compile failed for port ",
6519
410
            processed_init->port.value());
6520
410
    return tl::unexpected(port_component.error());
6521
410
  }
6522
109k
  url_pattern_.port_component = std::move(*port_component);
6523
6524
  // Let compileOptions be a copy of the default options with the ignore case
6525
  // property set to options["ignoreCase"].
6526
109k
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6527
109k
  if (options) {
6528
12.4k
    compile_options.ignore_case = options->ignore_case;
6529
12.4k
  }
6530
6531
  // TODO: Optimization opportunity: Simplify this if statement.
6532
  // If the result of running protocol component matches a special scheme given
6533
  // urlPattern's protocol component is true, then:
6534
109k
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6535
109k
          regex_provider>(url_pattern_.protocol_component)) {
6536
    // Let pathCompileOptions be copy of the pathname options with the ignore
6537
    // case property set to options["ignoreCase"].
6538
88.7k
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6539
88.7k
    if (options) {
6540
12.4k
      path_compile_options.ignore_case = options->ignore_case;
6541
12.4k
    }
6542
6543
    // Set urlPattern's pathname component to the result of compiling a
6544
    // component given processedInit["pathname"], canonicalize a pathname, and
6545
    // pathCompileOptions.
6546
88.7k
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6547
88.7k
        processed_init->pathname.value(),
6548
88.7k
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6549
88.7k
    if (!pathname_component) {
6550
1.04k
      ada_log("url_pattern_component::compile failed for pathname ",
6551
1.04k
              processed_init->pathname.value());
6552
1.04k
      return tl::unexpected(pathname_component.error());
6553
1.04k
    }
6554
87.7k
    url_pattern_.pathname_component = std::move(*pathname_component);
6555
87.7k
  } else {
6556
    // Otherwise set urlPattern's pathname component to the result of compiling
6557
    // a component given processedInit["pathname"], canonicalize an opaque
6558
    // pathname, and compileOptions.
6559
20.9k
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6560
20.9k
        processed_init->pathname.value(),
6561
20.9k
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6562
20.9k
    if (!pathname_component) {
6563
357
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6564
357
              processed_init->pathname.value());
6565
357
      return tl::unexpected(pathname_component.error());
6566
357
    }
6567
20.5k
    url_pattern_.pathname_component = std::move(*pathname_component);
6568
20.5k
  }
6569
6570
  // Set urlPattern's search component to the result of compiling a component
6571
  // given processedInit["search"], canonicalize a search, and compileOptions.
6572
108k
  auto search_component = url_pattern_component<regex_provider>::compile(
6573
108k
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6574
108k
      compile_options);
6575
108k
  if (!search_component) {
6576
96
    ada_log("url_pattern_component::compile failed for search ",
6577
96
            processed_init->search.value());
6578
96
    return tl::unexpected(search_component.error());
6579
96
  }
6580
108k
  url_pattern_.search_component = std::move(*search_component);
6581
6582
  // Set urlPattern's hash component to the result of compiling a component
6583
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6584
108k
  auto hash_component = url_pattern_component<regex_provider>::compile(
6585
108k
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6586
108k
      compile_options);
6587
108k
  if (!hash_component) {
6588
105
    ada_log("url_pattern_component::compile failed for hash ",
6589
105
            processed_init->hash.value());
6590
105
    return tl::unexpected(hash_component.error());
6591
105
  }
6592
108k
  url_pattern_.hash_component = std::move(*hash_component);
6593
6594
  // Return urlPattern.
6595
108k
  return url_pattern_;
6596
108k
}
6597
#endif  // ADA_INCLUDE_URL_PATTERN
6598
6599
}  // namespace ada::parser
6600
6601
#endif  // ADA_PARSER_INL_H
6602
/* end file include/ada/parser-inl.h */
6603
/* begin file include/ada/scheme-inl.h */
6604
/**
6605
 * @file scheme-inl.h
6606
 * @brief Definitions for the URL scheme.
6607
 */
6608
#ifndef ADA_SCHEME_INL_H
6609
#define ADA_SCHEME_INL_H
6610
6611
6612
namespace ada::scheme {
6613
6614
/**
6615
 * @namespace ada::scheme::details
6616
 * @brief Includes the definitions for scheme specific entities
6617
 */
6618
namespace details {
6619
// for use with is_special and get_special_port
6620
// Spaces, if present, are removed from URL.
6621
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6622
                                                "ftp",  "wss", "file",  " "};
6623
// for use with get_special_port
6624
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6625
6626
// @private
6627
// convert a string_view to a 64-bit integer key for fast comparison
6628
0
constexpr uint64_t make_key(std::string_view sv) {
6629
0
  uint64_t val = 0;
6630
0
  for (size_t i = 0; i < sv.size(); i++)
6631
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6632
0
  return val;
6633
0
}
6634
// precomputed keys for the special schemes, indexed by a hash of the input
6635
// string
6636
constexpr uint64_t scheme_keys[] = {
6637
    make_key("http"),   // 0: HTTP
6638
    0,                  // 1: sentinel
6639
    make_key("https"),  // 2: HTTPS
6640
    make_key("ws"),     // 3: WS
6641
    make_key("ftp"),    // 4: FTP
6642
    make_key("wss"),    // 5: WSS
6643
    make_key("file"),   // 6: FILE
6644
    0,                  // 7: sentinel
6645
};
6646
6647
// @private
6648
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6649
// n < 5
6650
1.48M
inline uint64_t branchless_load5(const char* p, size_t n) {
6651
1.48M
  uint64_t input = (uint8_t)p[0];
6652
1.48M
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6653
1.48M
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6654
1.48M
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6655
1.48M
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6656
1.48M
  return input;
6657
1.48M
}
6658
}  // namespace details
6659
6660
/****
6661
 * @private
6662
 * In is_special, get_scheme_type, and get_special_port, we
6663
 * use a standard hashing technique to find the index of the scheme in
6664
 * the is_special_list. The hashing technique is based on the size of
6665
 * the scheme and the first character of the scheme. It ensures that we
6666
 * do at most one string comparison per call. If the protocol is
6667
 * predictible (e.g., it is always "http"), we can get a better average
6668
 * performance by using a simpler approach where we loop and compare
6669
 * scheme with all possible protocols starting with the most likely
6670
 * protocol. Doing multiple comparisons may have a poor worst case
6671
 * performance, however. In this instance, we choose a potentially
6672
 * slightly lower best-case performance for a better worst-case
6673
 * performance. We can revisit this choice at any time.
6674
 *
6675
 * Reference:
6676
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6677
 * More C++ gems 17 (2000).
6678
 *
6679
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6680
 *
6681
 * Reference: https://github.com/ada-url/ada/issues/617
6682
 ****/
6683
6684
224k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6685
224k
  if (scheme.empty()) {
6686
21.5k
    return false;
6687
21.5k
  }
6688
202k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6689
202k
  const std::string_view target = details::is_special_list[hash_value];
6690
202k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6691
224k
}
6692
63.3k
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6693
63.3k
  if (scheme.empty()) {
6694
0
    return 0;
6695
0
  }
6696
63.3k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6697
63.3k
  const std::string_view target = details::is_special_list[hash_value];
6698
63.3k
  if (scheme.size() == target.size() &&
6699
63.3k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6700
63.3k
          details::scheme_keys[hash_value]) {
6701
63.3k
    return details::special_ports[hash_value];
6702
63.3k
  } else {
6703
0
    return 0;
6704
0
  }
6705
63.3k
}
6706
88.5k
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6707
88.5k
  return details::special_ports[int(type)];
6708
88.5k
}
6709
1.46M
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6710
1.46M
  if (scheme.empty()) {
6711
0
    return ada::scheme::NOT_SPECIAL;
6712
0
  }
6713
1.46M
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6714
1.46M
  const std::string_view target = details::is_special_list[hash_value];
6715
1.46M
  if (scheme.size() == target.size() &&
6716
1.42M
      details::branchless_load5(scheme.data(), scheme.size()) ==
6717
1.42M
          details::scheme_keys[hash_value]) {
6718
921k
    return ada::scheme::type(hash_value);
6719
921k
  } else {
6720
542k
    return ada::scheme::NOT_SPECIAL;
6721
542k
  }
6722
1.46M
}
6723
6724
}  // namespace ada::scheme
6725
6726
#endif  // ADA_SCHEME_INL_H
6727
/* end file include/ada/scheme-inl.h */
6728
/* begin file include/ada/serializers.h */
6729
/**
6730
 * @file serializers.h
6731
 * @brief IP address serialization utilities.
6732
 *
6733
 * This header provides functions for converting IP addresses to their
6734
 * string representations according to the WHATWG URL Standard.
6735
 */
6736
#ifndef ADA_SERIALIZERS_H
6737
#define ADA_SERIALIZERS_H
6738
6739
6740
#include <array>
6741
#include <string>
6742
6743
/**
6744
 * @namespace ada::serializers
6745
 * @brief IP address serialization functions.
6746
 *
6747
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6748
 */
6749
namespace ada::serializers {
6750
6751
/**
6752
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6753
 * Used for :: compression in IPv6 serialization.
6754
 *
6755
 * @param address The 8 16-bit pieces of the IPv6 address.
6756
 * @param[out] compress Index of the start of the longest zero sequence.
6757
 * @param[out] compress_length Length of the longest zero sequence.
6758
 */
6759
void find_longest_sequence_of_ipv6_pieces(
6760
    const std::array<uint16_t, 8>& address, size_t& compress,
6761
    size_t& compress_length) noexcept;
6762
6763
/**
6764
 * Serializes an IPv6 address to its string representation.
6765
 *
6766
 * @param address The 8 16-bit pieces of the IPv6 address.
6767
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6768
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6769
 */
6770
std::string ipv6(const std::array<uint16_t, 8>& address);
6771
6772
/**
6773
 * Serializes an IPv4 address to its dotted-decimal string representation.
6774
 *
6775
 * @param address The 32-bit IPv4 address as an integer.
6776
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6777
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6778
 */
6779
std::string ipv4(uint64_t address);
6780
6781
}  // namespace ada::serializers
6782
6783
#endif  // ADA_SERIALIZERS_H
6784
/* end file include/ada/serializers.h */
6785
/* begin file include/ada/state.h */
6786
/**
6787
 * @file state.h
6788
 * @brief URL parser state machine states.
6789
 *
6790
 * Defines the states used by the URL parsing state machine as specified
6791
 * in the WHATWG URL Standard.
6792
 *
6793
 * @see https://url.spec.whatwg.org/#url-parsing
6794
 */
6795
#ifndef ADA_STATE_H
6796
#define ADA_STATE_H
6797
6798
6799
#include <string>
6800
6801
namespace ada {
6802
6803
/**
6804
 * @brief States in the URL parsing state machine.
6805
 *
6806
 * The URL parser processes input through a sequence of states, each handling
6807
 * a specific part of the URL syntax.
6808
 *
6809
 * @see https://url.spec.whatwg.org/#url-parsing
6810
 */
6811
enum class state {
6812
  /**
6813
   * @see https://url.spec.whatwg.org/#authority-state
6814
   */
6815
  AUTHORITY,
6816
6817
  /**
6818
   * @see https://url.spec.whatwg.org/#scheme-start-state
6819
   */
6820
  SCHEME_START,
6821
6822
  /**
6823
   * @see https://url.spec.whatwg.org/#scheme-state
6824
   */
6825
  SCHEME,
6826
6827
  /**
6828
   * @see https://url.spec.whatwg.org/#host-state
6829
   */
6830
  HOST,
6831
6832
  /**
6833
   * @see https://url.spec.whatwg.org/#no-scheme-state
6834
   */
6835
  NO_SCHEME,
6836
6837
  /**
6838
   * @see https://url.spec.whatwg.org/#fragment-state
6839
   */
6840
  FRAGMENT,
6841
6842
  /**
6843
   * @see https://url.spec.whatwg.org/#relative-state
6844
   */
6845
  RELATIVE_SCHEME,
6846
6847
  /**
6848
   * @see https://url.spec.whatwg.org/#relative-slash-state
6849
   */
6850
  RELATIVE_SLASH,
6851
6852
  /**
6853
   * @see https://url.spec.whatwg.org/#file-state
6854
   */
6855
  FILE,
6856
6857
  /**
6858
   * @see https://url.spec.whatwg.org/#file-host-state
6859
   */
6860
  FILE_HOST,
6861
6862
  /**
6863
   * @see https://url.spec.whatwg.org/#file-slash-state
6864
   */
6865
  FILE_SLASH,
6866
6867
  /**
6868
   * @see https://url.spec.whatwg.org/#path-or-authority-state
6869
   */
6870
  PATH_OR_AUTHORITY,
6871
6872
  /**
6873
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
6874
   */
6875
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
6876
6877
  /**
6878
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
6879
   */
6880
  SPECIAL_AUTHORITY_SLASHES,
6881
6882
  /**
6883
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
6884
   */
6885
  SPECIAL_RELATIVE_OR_AUTHORITY,
6886
6887
  /**
6888
   * @see https://url.spec.whatwg.org/#query-state
6889
   */
6890
  QUERY,
6891
6892
  /**
6893
   * @see https://url.spec.whatwg.org/#path-state
6894
   */
6895
  PATH,
6896
6897
  /**
6898
   * @see https://url.spec.whatwg.org/#path-start-state
6899
   */
6900
  PATH_START,
6901
6902
  /**
6903
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
6904
   */
6905
  OPAQUE_PATH,
6906
6907
  /**
6908
   * @see https://url.spec.whatwg.org/#port-state
6909
   */
6910
  PORT,
6911
};
6912
6913
/**
6914
 * Converts a parser state to its string name for debugging.
6915
 * @param s The state to convert.
6916
 * @return A string representation of the state.
6917
 */
6918
ada_warn_unused std::string to_string(ada::state s);
6919
6920
}  // namespace ada
6921
6922
#endif  // ADA_STATE_H
6923
/* end file include/ada/state.h */
6924
/* begin file include/ada/unicode.h */
6925
/**
6926
 * @file unicode.h
6927
 * @brief Definitions for all unicode specific functions.
6928
 */
6929
#ifndef ADA_UNICODE_H
6930
#define ADA_UNICODE_H
6931
6932
6933
#include <string>
6934
#include <string_view>
6935
#include <optional>
6936
6937
/**
6938
 * Unicode operations. These functions are not part of our public API and may
6939
 * change at any time.
6940
 *
6941
 * @private
6942
 * @namespace ada::unicode
6943
 * @brief Includes the definitions for unicode operations
6944
 */
6945
namespace ada::unicode {
6946
6947
/**
6948
 * @private
6949
 * We receive a UTF-8 string representing a domain name.
6950
 * If the string is percent encoded, we apply percent decoding.
6951
 *
6952
 * Given a domain, we need to identify its labels.
6953
 * They are separated by label-separators:
6954
 *
6955
 * U+002E (.) FULL STOP
6956
 * U+FF0E FULLWIDTH FULL STOP
6957
 * U+3002 IDEOGRAPHIC FULL STOP
6958
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
6959
 *
6960
 * They are all mapped to U+002E.
6961
 *
6962
 * We process each label into a string that should not exceed 63 octets.
6963
 * If the string is already punycode (starts with "xn--"), then we must
6964
 * scan it to look for unallowed code points.
6965
 * Otherwise, if the string is not pure ASCII, we need to transcode it
6966
 * to punycode by following RFC 3454 which requires us to
6967
 * - Map characters  (see section 3),
6968
 * - Normalize (see section 4),
6969
 * - Reject forbidden characters,
6970
 * - Check for right-to-left characters and if so, check all requirements (see
6971
 * section 6),
6972
 * - Optionally reject based on unassigned code points (section 7).
6973
 *
6974
 * The Unicode standard provides a table of code points with a mapping, a list
6975
 * of forbidden code points and so forth. This table is subject to change and
6976
 * will vary based on the implementation. For Unicode 15, the table is at
6977
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
6978
 * If you use ICU, they parse this table and map it to code using a Python
6979
 * script.
6980
 *
6981
 * The resulting strings should not exceed 255 octets according to RFC 1035
6982
 * section 2.3.4. ICU checks for label size and domain size, but these errors
6983
 * are ignored.
6984
 *
6985
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
6986
 *
6987
 */
6988
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
6989
              size_t first_percent);
6990
6991
/**
6992
 * @private
6993
 * Checks if the input has tab or newline characters.
6994
 *
6995
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
6996
 * enough that compilers like GCC can 'autovectorize it'.
6997
 */
6998
ada_really_inline bool has_tabs_or_newline(
6999
    std::string_view user_input) noexcept;
7000
7001
/**
7002
 * @private
7003
 * Checks if the input is a forbidden host code point.
7004
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7005
 */
7006
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7007
7008
/**
7009
 * @private
7010
 * Checks if the input contains a forbidden domain code point.
7011
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7012
 */
7013
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7014
    const char* input, size_t length) noexcept;
7015
7016
/**
7017
 * @private
7018
 * Checks if the input contains a forbidden domain code point in which case
7019
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7020
 * then the second bit is set to 1.
7021
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7022
 */
7023
ada_really_inline constexpr uint8_t
7024
contains_forbidden_domain_code_point_or_upper(const char* input,
7025
                                              size_t length) noexcept;
7026
7027
/**
7028
 * @private
7029
 * Checks if the input is a forbidden domain code point.
7030
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7031
 */
7032
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7033
    char c) noexcept;
7034
7035
/**
7036
 * @private
7037
 * Checks if the input is alphanumeric, '+', '-' or '.'
7038
 */
7039
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7040
7041
/**
7042
 * @private
7043
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7044
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7045
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7046
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7047
 */
7048
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7049
7050
/**
7051
 * @private
7052
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7053
 * inclusive.
7054
 */
7055
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7056
7057
/**
7058
 * @private
7059
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7060
 * character.
7061
 */
7062
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7063
7064
/**
7065
 * @private
7066
 * Checks if the input is a C0 control or space character.
7067
 *
7068
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7069
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7070
 * SEPARATOR ONE, inclusive.
7071
 */
7072
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7073
7074
/**
7075
 * @private
7076
 * Checks if the input is a ASCII tab or newline character.
7077
 *
7078
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7079
 */
7080
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7081
7082
/**
7083
 * @private
7084
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7085
 * match for ".%2e", "%2e.", or "%2e%2e".
7086
 */
7087
ada_really_inline constexpr bool is_double_dot_path_segment(
7088
    std::string_view input) noexcept;
7089
7090
/**
7091
 * @private
7092
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7093
 * match for "%2e".
7094
 */
7095
ada_really_inline constexpr bool is_single_dot_path_segment(
7096
    std::string_view input) noexcept;
7097
7098
/**
7099
 * @private
7100
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7101
 */
7102
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7103
7104
/**
7105
 * @private
7106
 * @details Convert hex to binary. Caller is responsible to ensure that
7107
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7108
 */
7109
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7110
7111
/**
7112
 * @private
7113
 * first_percent should be  = input.find('%')
7114
 *
7115
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7116
 * @author Node.js
7117
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7118
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7119
 */
7120
std::string percent_decode(std::string_view input, size_t first_percent);
7121
7122
/**
7123
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7124
 * then percent-decode. Single allocation; no intermediate string.
7125
 *
7126
 * @param input A form-urlencoded component (key or value).
7127
 * @return The decoded string.
7128
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7129
 */
7130
std::string form_urlencoded_decode(std::string_view input);
7131
7132
/**
7133
 * @private
7134
 * Returns a percent-encoding string whether percent encoding was needed or not.
7135
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7136
 */
7137
std::string percent_encode(std::string_view input,
7138
                           const uint8_t character_set[]);
7139
/**
7140
 * @private
7141
 * Returns a percent-encoded string version of input, while starting the percent
7142
 * encoding at the provided index.
7143
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7144
 */
7145
std::string percent_encode(std::string_view input,
7146
                           const uint8_t character_set[], size_t index);
7147
/**
7148
 * @private
7149
 * Returns true if percent encoding was needed, in which case, we store
7150
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7151
 * true, the content is appended to 'out'.
7152
 * If percent encoding is not needed, out is left unchanged.
7153
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7154
 */
7155
template <bool append>
7156
bool percent_encode(std::string_view input, const uint8_t character_set[],
7157
                    std::string& out);
7158
/**
7159
 * @private
7160
 * Returns the index at which percent encoding should start, or (equivalently),
7161
 * the length of the prefix that does not require percent encoding.
7162
 */
7163
ada_really_inline size_t percent_encode_index(std::string_view input,
7164
                                              const uint8_t character_set[]);
7165
/**
7166
 * @private
7167
 * Lowers the string in-place, assuming that the content is ASCII.
7168
 * Return true if the content was ASCII.
7169
 */
7170
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7171
}  // namespace ada::unicode
7172
7173
#endif  // ADA_UNICODE_H
7174
/* end file include/ada/unicode.h */
7175
/* begin file include/ada/url_base-inl.h */
7176
/**
7177
 * @file url_base-inl.h
7178
 * @brief Inline functions for url base
7179
 */
7180
#ifndef ADA_URL_BASE_INL_H
7181
#define ADA_URL_BASE_INL_H
7182
7183
7184
#include <string>
7185
#if ADA_REGULAR_VISUAL_STUDIO
7186
#include <intrin.h>
7187
#endif  // ADA_REGULAR_VISUAL_STUDIO
7188
7189
namespace ada {
7190
7191
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7192
7.29M
    const noexcept {
7193
7.29M
  return type != ada::scheme::NOT_SPECIAL;
7194
7.29M
}
7195
7196
7.48k
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7197
7.48k
  return ada::scheme::get_special_port(type);
7198
7.48k
}
7199
7200
[[nodiscard]] ada_really_inline uint16_t
7201
81.0k
url_base::scheme_default_port() const noexcept {
7202
81.0k
  return scheme::get_special_port(type);
7203
81.0k
}
7204
7205
}  // namespace ada
7206
7207
#endif  // ADA_URL_BASE_INL_H
7208
/* end file include/ada/url_base-inl.h */
7209
/* begin file include/ada/url-inl.h */
7210
/**
7211
 * @file url-inl.h
7212
 * @brief Definitions for the URL
7213
 */
7214
#ifndef ADA_URL_INL_H
7215
#define ADA_URL_INL_H
7216
7217
7218
#include <charconv>
7219
#include <cstring>
7220
#include <optional>
7221
#include <string>
7222
#if ADA_REGULAR_VISUAL_STUDIO
7223
#include <intrin.h>
7224
#endif  // ADA_REGULAR_VISUAL_STUDIO
7225
7226
namespace ada {
7227
763k
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7228
763k
  return !username.empty() || !password.empty();
7229
763k
}
7230
22.3k
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7231
22.3k
  return port.has_value();
7232
22.3k
}
7233
51.7k
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7234
51.7k
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7235
51.7k
}
7236
22.3k
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7237
22.3k
  if (!host.has_value()) {
7238
2.40k
    return false;
7239
2.40k
  }
7240
19.9k
  return host->empty();
7241
22.3k
}
7242
22.3k
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7243
22.3k
  return host.has_value();
7244
22.3k
}
7245
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7246
0
  return out << u.to_string();
7247
0
}
7248
7249
22.3k
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7250
22.3k
  return path.size();
7251
22.3k
}
7252
7253
89.1k
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7254
89.1k
  return path;
7255
89.1k
}
7256
7257
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7258
22.3k
    const {
7259
22.3k
  url_components out{};
7260
7261
  // protocol ends with ':'. for example: "https:"
7262
22.3k
  out.protocol_end = uint32_t(get_protocol().size());
7263
7264
  // Trailing index is always the next character of the current one.
7265
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7266
22.3k
  size_t running_index = out.protocol_end;
7267
7268
22.3k
  if (host.has_value()) {
7269
    // 2 characters for "//" and 1 character for starting index
7270
19.9k
    out.host_start = out.protocol_end + 2;
7271
7272
19.9k
    if (has_credentials()) {
7273
12.8k
      out.username_end = uint32_t(out.host_start + username.size());
7274
7275
12.8k
      out.host_start += uint32_t(username.size());
7276
7277
12.8k
      if (!password.empty()) {
7278
12.4k
        out.host_start += uint32_t(password.size() + 1);
7279
12.4k
      }
7280
7281
12.8k
      out.host_end = uint32_t(out.host_start + host->size());
7282
12.8k
    } else {
7283
7.09k
      out.username_end = out.host_start;
7284
7285
      // Host does not start with "@" if it does not include credentials.
7286
7.09k
      out.host_end = uint32_t(out.host_start + host->size()) - 1;
7287
7.09k
    }
7288
7289
19.9k
    running_index = out.host_end + 1;
7290
19.9k
  } else {
7291
    // Update host start and end date to the same index, since it does not
7292
    // exist.
7293
2.40k
    out.host_start = out.protocol_end;
7294
2.40k
    out.host_end = out.host_start;
7295
7296
2.40k
    if (!has_opaque_path && path.starts_with("//")) {
7297
      // If url's host is null, url does not have an opaque path, url's path's
7298
      // size is greater than 1, and url's path[0] is the empty string, then
7299
      // append U+002F (/) followed by U+002E (.) to output.
7300
66
      running_index = out.protocol_end + 2;
7301
2.33k
    } else {
7302
2.33k
      running_index = out.protocol_end;
7303
2.33k
    }
7304
2.40k
  }
7305
7306
22.3k
  if (port.has_value()) {
7307
2.24k
    out.port = *port;
7308
2.24k
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7309
2.24k
  }
7310
7311
22.3k
  out.pathname_start = uint32_t(running_index);
7312
7313
22.3k
  running_index += path.size();
7314
7315
22.3k
  if (query.has_value()) {
7316
13.7k
    out.search_start = uint32_t(running_index);
7317
13.7k
    running_index += get_search().size();
7318
13.7k
    if (get_search().empty()) {
7319
479
      running_index++;
7320
479
    }
7321
13.7k
  }
7322
7323
22.3k
  if (hash.has_value()) {
7324
13.6k
    out.hash_start = uint32_t(running_index);
7325
13.6k
  }
7326
7327
22.3k
  return out;
7328
22.3k
}
7329
7330
200
inline void url::update_base_hostname(std::string_view input) { host = input; }
7331
7332
22.6k
inline void url::update_unencoded_base_hash(std::string_view input) {
7333
  // We do the percent encoding
7334
22.6k
  hash = unicode::percent_encode(input,
7335
22.6k
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7336
22.6k
}
7337
7338
inline void url::update_base_search(std::string_view input,
7339
22.9k
                                    const uint8_t query_percent_encode_set[]) {
7340
22.9k
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7341
22.9k
}
7342
7343
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7344
0
  query = std::move(input);
7345
0
}
7346
7347
12.5k
inline void url::update_base_pathname(const std::string_view input) {
7348
12.5k
  path = input;
7349
12.5k
}
7350
7351
0
inline void url::update_base_username(const std::string_view input) {
7352
0
  username = input;
7353
0
}
7354
7355
0
inline void url::update_base_password(const std::string_view input) {
7356
0
  password = input;
7357
0
}
7358
7359
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7360
0
  port = input;
7361
0
}
7362
7363
37
constexpr void url::clear_pathname() { path.clear(); }
7364
7365
786
constexpr void url::clear_search() { query = std::nullopt; }
7366
7367
22.3k
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7368
22.3k
  return hash.has_value();
7369
22.3k
}
7370
7371
44.7k
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7372
44.7k
  return query.has_value();
7373
44.7k
}
7374
7375
4.25k
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7376
7377
15.2k
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7378
15.2k
  type = ada::scheme::get_scheme_type(new_scheme);
7379
  // We only move the 'scheme' if it is non-special.
7380
15.2k
  if (!is_special()) {
7381
8.94k
    non_special_scheme = std::move(new_scheme);
7382
8.94k
  }
7383
15.2k
}
7384
7385
0
constexpr void url::copy_scheme(ada::url&& u) {
7386
0
  non_special_scheme = u.non_special_scheme;
7387
0
  type = u.type;
7388
0
}
7389
7390
1.20k
constexpr void url::copy_scheme(const ada::url& u) {
7391
1.20k
  non_special_scheme = u.non_special_scheme;
7392
1.20k
  type = u.type;
7393
1.20k
}
7394
7395
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7396
0
  if (is_special() && host.has_value() && username.empty() &&
7397
0
      password.empty() && !port.has_value()) [[likely]] {
7398
0
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7399
0
    const size_t host_size = host->size();
7400
0
    const size_t path_size = path.size();
7401
0
    const size_t query_size = query.has_value() ? query->size() : 0;
7402
0
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7403
0
    const size_t total = scheme.size() + 3 + host_size + path_size +
7404
0
                         (query.has_value() ? query_size + 1 : 0) +
7405
0
                         (hash.has_value() ? hash_size + 1 : 0);
7406
0
    std::string output(total, '\0');
7407
0
    char* p = output.data();
7408
0
    std::memcpy(p, scheme.data(), scheme.size());
7409
0
    p += scheme.size();
7410
0
    p[0] = ':';
7411
0
    p[1] = '/';
7412
0
    p[2] = '/';
7413
0
    p += 3;
7414
0
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7415
0
    std::memcpy(p, host->data(), host_size);
7416
0
    p += host_size;
7417
0
    std::memcpy(p, path.data(), path_size);
7418
0
    p += path_size;
7419
0
    if (query.has_value()) {
7420
0
      *p++ = '?';
7421
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7422
0
      std::memcpy(p, query->data(), query_size);
7423
0
      p += query_size;
7424
0
    }
7425
0
    if (hash.has_value()) {
7426
0
      *p++ = '#';
7427
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7428
0
      std::memcpy(p, hash->data(), hash_size);
7429
0
    }
7430
0
    return output;
7431
0
  }
7432
0
7433
0
  std::string output;
7434
0
  output.reserve(get_href_size());
7435
0
7436
0
  if (is_special()) {
7437
0
    output.append(ada::scheme::details::is_special_list[type]);
7438
0
    output += ':';
7439
0
  } else {
7440
0
    output.append(non_special_scheme);
7441
0
    output += ':';
7442
0
  }
7443
0
7444
0
  if (host.has_value()) {
7445
0
    output += '/';
7446
0
    output += '/';
7447
0
    if (has_credentials()) {
7448
0
      output.append(username);
7449
0
      if (!password.empty()) {
7450
0
        output += ':';
7451
0
        output.append(password);
7452
0
      }
7453
0
      output += '@';
7454
0
    }
7455
0
    output.append(*host);
7456
0
    if (port.has_value()) {
7457
0
      output += ':';
7458
0
      char port_buf[5];
7459
0
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7460
0
      (void)ec;
7461
0
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7462
0
    }
7463
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7464
0
    // If url's host is null, url does not have an opaque path, url's path's
7465
0
    // size is greater than 1, and url's path[0] is the empty string, then
7466
0
    // append U+002F (/) followed by U+002E (.) to output.
7467
0
    output += '/';
7468
0
    output += '.';
7469
0
  }
7470
0
  output.append(path);
7471
0
  if (query.has_value()) {
7472
0
    output += '?';
7473
0
    output.append(*query);
7474
0
  }
7475
0
  if (hash.has_value()) {
7476
0
    output += '#';
7477
0
    output.append(*hash);
7478
0
  }
7479
0
  return output;
7480
0
}
7481
7482
630k
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7483
630k
  size_t size = 0;
7484
630k
  if (is_special()) {
7485
594k
    size += ada::scheme::details::is_special_list[type].size() + 1;
7486
594k
  } else {
7487
35.9k
    size += non_special_scheme.size() + 1;
7488
35.9k
  }
7489
630k
  if (host.has_value()) {
7490
600k
    size += host->size();
7491
600k
    size += 2;
7492
600k
    if (has_credentials()) {
7493
161k
      size += username.size();
7494
161k
      if (!password.empty()) {
7495
140k
        size += 1 + password.size();
7496
140k
      }
7497
161k
      size += 1;
7498
161k
    }
7499
600k
    if (port.has_value()) {
7500
92.0k
      size += 1;
7501
92.0k
      uint16_t p = *port;
7502
92.0k
      size += (p >= 10000)  ? 5
7503
92.0k
              : (p >= 1000) ? 4
7504
89.7k
              : (p >= 100)  ? 3
7505
12.1k
              : (p >= 10)   ? 2
7506
11.1k
                            : 1;
7507
92.0k
    }
7508
600k
  } else if (!has_opaque_path && path.starts_with("//")) {
7509
637
    size += 2;
7510
637
  }
7511
630k
  size += path.size();
7512
630k
  if (query.has_value()) {
7513
142k
    size += 1 + query->size();
7514
142k
  }
7515
630k
  if (hash.has_value()) {
7516
107k
    size += 1 + hash->size();
7517
107k
  }
7518
630k
  return size;
7519
630k
}
7520
7521
ada_really_inline size_t url::parse_port(std::string_view view,
7522
31.5k
                                         bool check_trailing_content) noexcept {
7523
31.5k
  ada_log("parse_port('", view, "') ", view.size());
7524
31.5k
  if (!view.empty() && view[0] == '-') {
7525
25
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7526
25
    is_valid = false;
7527
25
    return 0;
7528
25
  }
7529
31.5k
  uint16_t parsed_port{};
7530
31.5k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7531
31.5k
  if (r.ec == std::errc::result_out_of_range) {
7532
263
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7533
263
    is_valid = false;
7534
263
    return 0;
7535
263
  }
7536
31.2k
  ada_log("parse_port: ", parsed_port);
7537
31.2k
  const auto consumed = size_t(r.ptr - view.data());
7538
31.2k
  ada_log("parse_port: consumed ", consumed);
7539
31.2k
  if (check_trailing_content) {
7540
29.1k
    is_valid &=
7541
29.1k
        (consumed == view.size() || view[consumed] == '/' ||
7542
578
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7543
29.1k
  }
7544
31.2k
  ada_log("parse_port: is_valid = ", is_valid);
7545
31.2k
  if (is_valid) {
7546
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7547
30.9k
    auto default_port = scheme_default_port();
7548
30.9k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7549
30.8k
                         (default_port != parsed_port);
7550
30.9k
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7551
30.9k
                                                  : std::nullopt;
7552
30.9k
  }
7553
31.2k
  return consumed;
7554
31.5k
}
7555
7556
}  // namespace ada
7557
7558
#endif  // ADA_URL_H
7559
/* end file include/ada/url-inl.h */
7560
/* begin file include/ada/url_components-inl.h */
7561
/**
7562
 * @file url_components.h
7563
 * @brief Declaration for the URL Components
7564
 */
7565
#ifndef ADA_URL_COMPONENTS_INL_H
7566
#define ADA_URL_COMPONENTS_INL_H
7567
7568
7569
namespace ada {
7570
7571
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7572
0
    const noexcept {
7573
0
  /**
7574
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7575
0
   *       |     |    |          | ^^^^|       |   |
7576
0
   *       |     |    |          | |   |       |   `----- hash_start
7577
0
   *       |     |    |          | |   |       `--------- search_start
7578
0
   *       |     |    |          | |   `----------------- pathname_start
7579
0
   *       |     |    |          | `--------------------- port
7580
0
   *       |     |    |          `----------------------- host_end
7581
0
   *       |     |    `---------------------------------- host_start
7582
0
   *       |     `--------------------------------------- username_end
7583
0
   *       `--------------------------------------------- protocol_end
7584
0
   */
7585
0
  // These conditions can be made more strict.
7586
0
  if (protocol_end == url_components::omitted) {
7587
0
    return false;
7588
0
  }
7589
0
  uint32_t index = protocol_end;
7590
0
7591
0
  if (username_end == url_components::omitted) {
7592
0
    return false;
7593
0
  }
7594
0
  if (username_end < index) {
7595
0
    return false;
7596
0
  }
7597
0
  index = username_end;
7598
0
7599
0
  if (host_start == url_components::omitted) {
7600
0
    return false;
7601
0
  }
7602
0
  if (host_start < index) {
7603
0
    return false;
7604
0
  }
7605
0
  index = host_start;
7606
0
7607
0
  if (port != url_components::omitted) {
7608
0
    if (port > 0xffff) {
7609
0
      return false;
7610
0
    }
7611
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7612
0
    if (index + port_length < index) {
7613
0
      return false;
7614
0
    }
7615
0
    index += port_length;
7616
0
  }
7617
0
7618
0
  if (pathname_start == url_components::omitted) {
7619
0
    return false;
7620
0
  }
7621
0
  if (pathname_start < index) {
7622
0
    return false;
7623
0
  }
7624
0
  index = pathname_start;
7625
0
7626
0
  if (search_start != url_components::omitted) {
7627
0
    if (search_start < index) {
7628
0
      return false;
7629
0
    }
7630
0
    index = search_start;
7631
0
  }
7632
0
7633
0
  if (hash_start != url_components::omitted) {
7634
0
    if (hash_start < index) {
7635
0
      return false;
7636
0
    }
7637
0
  }
7638
0
7639
0
  return true;
7640
0
}
7641
7642
}  // namespace ada
7643
#endif
7644
/* end file include/ada/url_components-inl.h */
7645
/* begin file include/ada/url_aggregator.h */
7646
/**
7647
 * @file url_aggregator.h
7648
 * @brief Declaration for the `ada::url_aggregator` class.
7649
 *
7650
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7651
 * URL using a single buffer with component offsets. This is the default and
7652
 * most memory-efficient URL representation in Ada.
7653
 *
7654
 * @see url.h for an alternative representation using separate strings
7655
 */
7656
#ifndef ADA_URL_AGGREGATOR_H
7657
#define ADA_URL_AGGREGATOR_H
7658
7659
#include <ostream>
7660
#include <string>
7661
#include <string_view>
7662
#include <variant>
7663
7664
7665
namespace ada {
7666
7667
namespace parser {}
7668
7669
/**
7670
 * @brief Memory-efficient URL representation using a single buffer.
7671
 *
7672
 * The `url_aggregator` stores the entire normalized URL in a single string
7673
 * buffer and tracks component boundaries using offsets. This design minimizes
7674
 * memory allocations and is ideal for read-mostly access patterns.
7675
 *
7676
 * Getter methods return `std::string_view` pointing into the internal buffer.
7677
 * These views are lightweight (no allocation) but become invalid if the
7678
 * url_aggregator is modified or destroyed.
7679
 *
7680
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7681
 * when any setter is called. Do not use a getter's result as input to a
7682
 * setter on the same object without copying first.
7683
 *
7684
 * @note This is the default URL type returned by `ada::parse()`.
7685
 *
7686
 * @see url For an alternative using separate std::string instances
7687
 */
7688
struct url_aggregator : url_base {
7689
2.09M
  url_aggregator() = default;
7690
354k
  url_aggregator(const url_aggregator& u) = default;
7691
1.06M
  url_aggregator(url_aggregator&& u) noexcept = default;
7692
228k
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7693
71.0k
  url_aggregator& operator=(const url_aggregator& u) = default;
7694
3.51M
  ~url_aggregator() override = default;
7695
7696
  /**
7697
   * The setter functions follow the steps defined in the URL Standard.
7698
   *
7699
   * The url_aggregator has a single buffer that contains the entire normalized
7700
   * URL. The various components are represented as offsets into that buffer.
7701
   * When you call get_pathname(), for example, you get a std::string_view that
7702
   * points into that buffer. If the url_aggregator is modified, the buffer may
7703
   * be reallocated, and the std::string_view you obtained earlier may become
7704
   * invalid. In particular, this implies that you cannot modify the URL using
7705
   * a setter function with a std::string_view that points into the
7706
   * url_aggregator E.g., the following is incorrect:
7707
   * url->set_hostname(url->get_pathname()).
7708
   * You must first copy the pathname to a separate string.
7709
   * std::string pathname(url->get_pathname());
7710
   * url->set_hostname(pathname);
7711
   *
7712
   * The caller is responsible for ensuring that the url_aggregator is not
7713
   * modified while any std::string_view obtained from it is in use.
7714
   */
7715
  bool set_href(std::string_view input);
7716
  bool set_host(std::string_view input);
7717
  bool set_hostname(std::string_view input);
7718
  bool set_protocol(std::string_view input);
7719
  bool set_username(std::string_view input);
7720
  bool set_password(std::string_view input);
7721
  bool set_port(std::string_view input);
7722
  bool set_pathname(std::string_view input);
7723
  void set_search(std::string_view input);
7724
  void set_hash(std::string_view input);
7725
7726
  /**
7727
   * Validates whether the hostname is a valid domain according to RFC 1034.
7728
   * @return `true` if the domain is valid, `false` otherwise.
7729
   */
7730
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7731
7732
  /**
7733
   * Returns the URL's origin (scheme + host + port for special URLs).
7734
   * @return A newly allocated string containing the serialized origin.
7735
   * @see https://url.spec.whatwg.org/#concept-url-origin
7736
   */
7737
  [[nodiscard]] std::string get_origin() const override;
7738
7739
  /**
7740
   * Returns the full serialized URL (the href) as a string_view.
7741
   * Does not allocate memory. The returned view becomes invalid if this
7742
   * url_aggregator is modified or destroyed.
7743
   * @return A string_view into the internal buffer.
7744
   * @see https://url.spec.whatwg.org/#dom-url-href
7745
   */
7746
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7747
      ada_lifetime_bound;
7748
7749
  /**
7750
   * Returns the byte length of the serialized URL without allocating a string.
7751
   * @return Size of the href in bytes.
7752
   */
7753
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7754
7755
  /**
7756
   * Returns the URL's username component.
7757
   * Does not allocate memory. The returned view becomes invalid if this
7758
   * url_aggregator is modified or destroyed.
7759
   * @return A string_view of the username.
7760
   * @see https://url.spec.whatwg.org/#dom-url-username
7761
   */
7762
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7763
7764
  /**
7765
   * Returns the URL's password component.
7766
   * Does not allocate memory. The returned view becomes invalid if this
7767
   * url_aggregator is modified or destroyed.
7768
   * @return A string_view of the password.
7769
   * @see https://url.spec.whatwg.org/#dom-url-password
7770
   */
7771
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
7772
7773
  /**
7774
   * Returns the URL's port as a string (e.g., "8080").
7775
   * Does not allocate memory. Returns empty view if no port is set.
7776
   * The returned view becomes invalid if this url_aggregator is modified.
7777
   * @return A string_view of the port.
7778
   * @see https://url.spec.whatwg.org/#dom-url-port
7779
   */
7780
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
7781
7782
  /**
7783
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
7784
   * Does not allocate memory. Returns empty view if no fragment is set.
7785
   * The returned view becomes invalid if this url_aggregator is modified.
7786
   * @return A string_view of the hash.
7787
   * @see https://url.spec.whatwg.org/#dom-url-hash
7788
   */
7789
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
7790
7791
  /**
7792
   * Returns the URL's host and port (e.g., "example.com:8080").
7793
   * Does not allocate memory. Returns empty view if no host is set.
7794
   * The returned view becomes invalid if this url_aggregator is modified.
7795
   * @return A string_view of host:port.
7796
   * @see https://url.spec.whatwg.org/#dom-url-host
7797
   */
7798
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
7799
7800
  /**
7801
   * Returns the URL's hostname (without port).
7802
   * Does not allocate memory. Returns empty view if no host is set.
7803
   * The returned view becomes invalid if this url_aggregator is modified.
7804
   * @return A string_view of the hostname.
7805
   * @see https://url.spec.whatwg.org/#dom-url-hostname
7806
   */
7807
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
7808
7809
  /**
7810
   * Returns the URL's path component.
7811
   * Does not allocate memory. The returned view becomes invalid if this
7812
   * url_aggregator is modified or destroyed.
7813
   * @return A string_view of the pathname.
7814
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7815
   */
7816
  [[nodiscard]] constexpr std::string_view get_pathname() const
7817
      ada_lifetime_bound;
7818
7819
  /**
7820
   * Returns the byte length of the pathname without creating a string.
7821
   * @return Size of the pathname in bytes.
7822
   * @see https://url.spec.whatwg.org/#dom-url-pathname
7823
   */
7824
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
7825
7826
  /**
7827
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
7828
   * Does not allocate memory. Returns empty view if no query is set.
7829
   * The returned view becomes invalid if this url_aggregator is modified.
7830
   * @return A string_view of the search/query.
7831
   * @see https://url.spec.whatwg.org/#dom-url-search
7832
   */
7833
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
7834
7835
  /**
7836
   * Returns the URL's scheme followed by a colon (e.g., "https:").
7837
   * Does not allocate memory. The returned view becomes invalid if this
7838
   * url_aggregator is modified or destroyed.
7839
   * @return A string_view of the protocol.
7840
   * @see https://url.spec.whatwg.org/#dom-url-protocol
7841
   */
7842
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
7843
7844
  /**
7845
   * Checks if the URL has credentials (non-empty username or password).
7846
   * @return `true` if username or password is non-empty, `false` otherwise.
7847
   */
7848
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
7849
      const noexcept;
7850
7851
  /**
7852
   * Returns the URL component offsets for efficient serialization.
7853
   *
7854
   * The components represent byte offsets into the serialized URL:
7855
   * ```
7856
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7857
   *       |     |    |          | ^^^^|       |   |
7858
   *       |     |    |          | |   |       |   `----- hash_start
7859
   *       |     |    |          | |   |       `--------- search_start
7860
   *       |     |    |          | |   `----------------- pathname_start
7861
   *       |     |    |          | `--------------------- port
7862
   *       |     |    |          `----------------------- host_end
7863
   *       |     |    `---------------------------------- host_start
7864
   *       |     `--------------------------------------- username_end
7865
   *       `--------------------------------------------- protocol_end
7866
   * ```
7867
   * @return A constant reference to the url_components struct.
7868
   * @see https://github.com/servo/rust-url
7869
   */
7870
  [[nodiscard]] ada_really_inline const url_components& get_components()
7871
      const noexcept;
7872
7873
  /**
7874
   * Returns a JSON string representation of this URL for debugging.
7875
   * @return A JSON-formatted string with all URL components.
7876
   */
7877
  [[nodiscard]] std::string to_string() const override;
7878
7879
  /**
7880
   * Returns a visual diagram showing component boundaries in the URL.
7881
   * Useful for debugging and understanding URL structure.
7882
   * @return A multi-line string diagram.
7883
   */
7884
  [[nodiscard]] std::string to_diagram() const;
7885
7886
  /**
7887
   * Validates internal consistency of component offsets (for debugging).
7888
   * @return `true` if offsets are consistent, `false` if corrupted.
7889
   */
7890
  [[nodiscard]] constexpr bool validate() const noexcept;
7891
7892
  /**
7893
   * Checks if the URL has an empty hostname (host is set but empty string).
7894
   * @return `true` if host exists but is empty, `false` otherwise.
7895
   */
7896
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
7897
7898
  /**
7899
   * Checks if the URL has a hostname (including empty hostnames).
7900
   * @return `true` if host is present, `false` otherwise.
7901
   */
7902
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
7903
7904
  /**
7905
   * Checks if the URL has a non-empty username.
7906
   * @return `true` if username is non-empty, `false` otherwise.
7907
   */
7908
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
7909
7910
  /**
7911
   * Checks if the URL has a non-empty password.
7912
   * @return `true` if password is non-empty, `false` otherwise.
7913
   */
7914
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
7915
7916
  /**
7917
   * Checks if the URL has a non-default port explicitly specified.
7918
   * @return `true` if a port is present, `false` otherwise.
7919
   */
7920
  [[nodiscard]] constexpr bool has_port() const noexcept;
7921
7922
  /**
7923
   * Checks if the URL has a password component (may be empty).
7924
   * @return `true` if password is present, `false` otherwise.
7925
   */
7926
  [[nodiscard]] constexpr bool has_password() const noexcept;
7927
7928
  /**
7929
   * Checks if the URL has a fragment/hash component.
7930
   * @return `true` if hash is present, `false` otherwise.
7931
   */
7932
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
7933
7934
  /**
7935
   * Checks if the URL has a query/search component.
7936
   * @return `true` if query is present, `false` otherwise.
7937
   */
7938
  [[nodiscard]] constexpr bool has_search() const noexcept override;
7939
7940
  /**
7941
   * Removes the port from the URL.
7942
   */
7943
  inline void clear_port();
7944
7945
  /**
7946
   * Removes the hash/fragment from the URL.
7947
   */
7948
  inline void clear_hash();
7949
7950
  /**
7951
   * Removes the query/search string from the URL.
7952
   */
7953
  inline void clear_search() override;
7954
7955
 private:
7956
  // helper methods
7957
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
7958
      url_aggregator& url);
7959
  // parse_url methods
7960
  friend url_aggregator parser::parse_url<url_aggregator>(
7961
      std::string_view, const url_aggregator*);
7962
7963
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
7964
      std::string_view, const url_aggregator*);
7965
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
7966
      std::string_view, const url_aggregator*);
7967
  template <class result_type>
7968
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
7969
7970
#if ADA_INCLUDE_URL_PATTERN
7971
  // url_pattern methods
7972
  template <url_pattern_regex::regex_concept regex_provider>
7973
  friend tl::expected<url_pattern<regex_provider>, errors>
7974
  parse_url_pattern_impl(
7975
      std::variant<std::string_view, url_pattern_init>&& input,
7976
      const std::string_view* base_url, const url_pattern_options* options);
7977
#endif  // ADA_INCLUDE_URL_PATTERN
7978
7979
  // components is declared before buffer so that the offset fields land
7980
  // close to url_base in memory, improving cache locality for getter calls.
7981
  // Note: exact cache-line placement is implementation- and platform-dependent.
7982
  url_components components{};
7983
  std::string buffer{};
7984
7985
  /**
7986
   * Returns true if neither the search, nor the hash nor the pathname
7987
   * have been set.
7988
   * @return true if the buffer is ready to receive the path.
7989
   */
7990
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
7991
7992
  inline void add_authority_slashes_if_needed();
7993
7994
  /**
7995
   * To optimize performance, you may indicate how much memory to allocate
7996
   * within this instance.
7997
   */
7998
  constexpr void reserve(uint32_t capacity);
7999
8000
  ada_really_inline size_t parse_port(std::string_view view,
8001
                                      bool check_trailing_content) override;
8002
8003
2.29k
  ada_really_inline size_t parse_port(std::string_view view) override {
8004
2.29k
    return this->parse_port(view, false);
8005
2.29k
  }
8006
8007
  /**
8008
   * Return true on success. The 'in_place' parameter indicates whether the
8009
   * the string_view input is pointing in the buffer. When in_place is false,
8010
   * we must nearly always update the buffer.
8011
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8012
   */
8013
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8014
8015
  /**
8016
   * Return true on success.
8017
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8018
   */
8019
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8020
8021
  /**
8022
   * Return true on success.
8023
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8024
   */
8025
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8026
8027
  ada_really_inline void parse_path(std::string_view input);
8028
8029
  /**
8030
   * A URL cannot have a username/password/port if its host is null or the empty
8031
   * string, or its scheme is "file".
8032
   */
8033
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8034
8035
  template <bool override_hostname = false>
8036
  bool set_host_or_hostname(std::string_view input);
8037
8038
  ada_really_inline bool parse_host(std::string_view input);
8039
8040
  inline void update_base_authority(std::string_view base_buffer,
8041
                                    const url_components& base);
8042
  inline void update_unencoded_base_hash(std::string_view input);
8043
  inline void update_base_hostname(std::string_view input);
8044
  inline void update_base_search(std::string_view input);
8045
  inline void update_base_search(std::string_view input,
8046
                                 const uint8_t* query_percent_encode_set);
8047
  inline void update_base_pathname(std::string_view input);
8048
  inline void update_base_username(std::string_view input);
8049
  inline void append_base_username(std::string_view input);
8050
  inline void update_base_password(std::string_view input);
8051
  inline void append_base_password(std::string_view input);
8052
  inline void update_base_port(uint32_t input);
8053
  inline void append_base_pathname(std::string_view input);
8054
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8055
  constexpr void clear_hostname();
8056
  constexpr void clear_password();
8057
  constexpr void clear_pathname() override;
8058
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8059
  void delete_dash_dot();
8060
  inline void consume_prepared_path(std::string_view input);
8061
  template <bool has_state_override = false>
8062
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8063
      std::string_view input);
8064
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8065
                                                std::string_view input);
8066
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8067
  constexpr void set_protocol_as_file();
8068
  inline void set_scheme(std::string_view new_scheme);
8069
  /**
8070
   * Fast function to set the scheme from a view with a colon in the
8071
   * buffer, does not change type.
8072
   */
8073
  inline void set_scheme_from_view_with_colon(
8074
      std::string_view new_scheme_with_colon);
8075
  inline void copy_scheme(const url_aggregator& u);
8076
8077
  inline void update_host_to_base_host(const std::string_view input);
8078
8079
};  // url_aggregator
8080
8081
inline std::ostream& operator<<(std::ostream& out, const url& u);
8082
}  // namespace ada
8083
8084
#endif
8085
/* end file include/ada/url_aggregator.h */
8086
/* begin file include/ada/url_aggregator-inl.h */
8087
/**
8088
 * @file url_aggregator-inl.h
8089
 * @brief Inline functions for url aggregator
8090
 */
8091
#ifndef ADA_URL_AGGREGATOR_INL_H
8092
#define ADA_URL_AGGREGATOR_INL_H
8093
8094
/* begin file include/ada/unicode-inl.h */
8095
/**
8096
 * @file unicode-inl.h
8097
 * @brief Definitions for unicode operations.
8098
 */
8099
#ifndef ADA_UNICODE_INL_H
8100
#define ADA_UNICODE_INL_H
8101
8102
/**
8103
 * Unicode operations. These functions are not part of our public API and may
8104
 * change at any time.
8105
 *
8106
 * private
8107
 * @namespace ada::unicode
8108
 * @brief Includes the declarations for unicode operations
8109
 */
8110
namespace ada::unicode {
8111
ada_really_inline size_t percent_encode_index(const std::string_view input,
8112
256k
                                              const uint8_t character_set[]) {
8113
256k
  const char* data = input.data();
8114
256k
  const size_t size = input.size();
8115
8116
  // Process 8 bytes at a time using unrolled loop
8117
256k
  size_t i = 0;
8118
509k
  for (; i + 8 <= size; i += 8) {
8119
265k
    unsigned char chunk[8];
8120
265k
    std::memcpy(&chunk, data + i,
8121
265k
                8);  // entices compiler to unconditionally process 8 characters
8122
8123
    // Check 8 characters at once
8124
2.30M
    for (size_t j = 0; j < 8; j++) {
8125
2.05M
      if (character_sets::bit_at(character_set, chunk[j])) {
8126
12.8k
        return i + j;
8127
12.8k
      }
8128
2.05M
    }
8129
265k
  }
8130
8131
  // Handle remaining bytes
8132
274k
  for (; i < size; i++) {
8133
42.6k
    if (character_sets::bit_at(character_set, data[i])) {
8134
12.2k
      return i;
8135
12.2k
    }
8136
42.6k
  }
8137
8138
231k
  return size;
8139
244k
}
8140
}  // namespace ada::unicode
8141
8142
#endif  // ADA_UNICODE_INL_H
8143
/* end file include/ada/unicode-inl.h */
8144
8145
#include <charconv>
8146
#include <ostream>
8147
#include <string_view>
8148
8149
namespace ada {
8150
8151
inline void url_aggregator::update_base_authority(
8152
11.9k
    std::string_view base_buffer, const ada::url_components& base) {
8153
11.9k
  std::string_view input = base_buffer.substr(
8154
11.9k
      base.protocol_end, base.host_start - base.protocol_end);
8155
11.9k
  ada_log("url_aggregator::update_base_authority ", input);
8156
8157
11.9k
  bool input_starts_with_dash = input.starts_with("//");
8158
11.9k
  uint32_t diff = components.host_start - components.protocol_end;
8159
8160
11.9k
  buffer.erase(components.protocol_end,
8161
11.9k
               components.host_start - components.protocol_end);
8162
11.9k
  components.username_end = components.protocol_end;
8163
8164
11.9k
  if (input_starts_with_dash) {
8165
10.8k
    input.remove_prefix(2);
8166
10.8k
    diff += 2;  // add "//"
8167
10.8k
    buffer.insert(components.protocol_end, "//");
8168
10.8k
    components.username_end += 2;
8169
10.8k
  }
8170
8171
11.9k
  size_t password_delimiter = input.find(':');
8172
8173
  // Check if input contains both username and password by checking the
8174
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8175
11.9k
  if (password_delimiter != std::string_view::npos) {
8176
    // Insert both username and password
8177
172
    std::string_view username = input.substr(0, password_delimiter);
8178
172
    std::string_view password = input.substr(password_delimiter + 1);
8179
8180
172
    buffer.insert(components.protocol_end + diff, username);
8181
172
    diff += uint32_t(username.size());
8182
172
    buffer.insert(components.protocol_end + diff, ":");
8183
172
    components.username_end = components.protocol_end + diff;
8184
172
    buffer.insert(components.protocol_end + diff + 1, password);
8185
172
    diff += uint32_t(password.size()) + 1;
8186
11.7k
  } else if (!input.empty()) {
8187
    // Insert only username
8188
255
    buffer.insert(components.protocol_end + diff, input);
8189
255
    components.username_end =
8190
255
        components.protocol_end + diff + uint32_t(input.size());
8191
255
    diff += uint32_t(input.size());
8192
255
  }
8193
8194
11.9k
  components.host_start += diff;
8195
8196
11.9k
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8197
0
    buffer.insert(components.host_start, "@");
8198
0
    diff++;
8199
0
  }
8200
11.9k
  components.host_end += diff;
8201
11.9k
  components.pathname_start += diff;
8202
11.9k
  if (components.search_start != url_components::omitted) {
8203
0
    components.search_start += diff;
8204
0
  }
8205
11.9k
  if (components.hash_start != url_components::omitted) {
8206
0
    components.hash_start += diff;
8207
0
  }
8208
11.9k
}
8209
8210
46.1k
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8211
46.1k
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8212
46.1k
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8213
46.1k
          " bytes] components.hash_start = ", components.hash_start);
8214
46.1k
  ADA_ASSERT_TRUE(validate());
8215
46.1k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8216
46.1k
  if (components.hash_start != url_components::omitted) {
8217
254
    buffer.resize(components.hash_start);
8218
254
  }
8219
46.1k
  components.hash_start = uint32_t(buffer.size());
8220
46.1k
  buffer += "#";
8221
46.1k
  bool encoding_required = unicode::percent_encode<true>(
8222
46.1k
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8223
  // When encoding_required is false, then buffer is left unchanged, and percent
8224
  // encoding was not deemed required.
8225
46.1k
  if (!encoding_required) {
8226
35.3k
    buffer.append(input);
8227
35.3k
  }
8228
46.1k
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8229
46.1k
          buffer, "' [", buffer.size(), " bytes]");
8230
46.1k
  ADA_ASSERT_TRUE(validate());
8231
46.1k
}
8232
8233
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8234
814k
    uint32_t start, uint32_t end, std::string_view input) {
8235
814k
  uint32_t current_length = end - start;
8236
814k
  uint32_t input_size = uint32_t(input.size());
8237
814k
  uint32_t new_difference = input_size - current_length;
8238
8239
814k
  if (current_length == 0) {
8240
693k
    buffer.insert(start, input);
8241
693k
  } else if (input_size == current_length) {
8242
21.2k
    buffer.replace(start, input_size, input);
8243
99.6k
  } else if (input_size < current_length) {
8244
21.7k
    buffer.erase(start, current_length - input_size);
8245
21.7k
    buffer.replace(start, input_size, input);
8246
77.8k
  } else {
8247
77.8k
    buffer.replace(start, current_length, input.substr(0, current_length));
8248
77.8k
    buffer.insert(start + current_length, input.substr(current_length));
8249
77.8k
  }
8250
8251
814k
  return new_difference;
8252
814k
}
8253
8254
627k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8255
627k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8256
627k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8257
627k
  ADA_ASSERT_TRUE(validate());
8258
627k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8259
8260
  // This next line is required for when parsing a URL like `foo://`
8261
627k
  add_authority_slashes_if_needed();
8262
8263
627k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8264
627k
  uint32_t new_difference =
8265
627k
      replace_and_resize(components.host_start, components.host_end, input);
8266
8267
627k
  if (has_credentials) {
8268
67.5k
    buffer.insert(components.host_start, "@");
8269
67.5k
    new_difference++;
8270
67.5k
  }
8271
627k
  components.host_end += new_difference;
8272
627k
  components.pathname_start += new_difference;
8273
627k
  if (components.search_start != url_components::omitted) {
8274
794
    components.search_start += new_difference;
8275
794
  }
8276
627k
  if (components.hash_start != url_components::omitted) {
8277
783
    components.hash_start += new_difference;
8278
783
  }
8279
627k
  ADA_ASSERT_TRUE(validate());
8280
627k
}
8281
8282
[[nodiscard]] ada_really_inline uint32_t
8283
206k
url_aggregator::get_pathname_length() const noexcept {
8284
206k
  ada_log("url_aggregator::get_pathname_length");
8285
206k
  uint32_t ending_index = uint32_t(buffer.size());
8286
206k
  if (components.search_start != url_components::omitted) {
8287
16.9k
    ending_index = components.search_start;
8288
189k
  } else if (components.hash_start != url_components::omitted) {
8289
867
    ending_index = components.hash_start;
8290
867
  }
8291
206k
  return ending_index - components.pathname_start;
8292
206k
}
8293
8294
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8295
443k
    const noexcept {
8296
443k
  return buffer.size() == components.pathname_start;
8297
443k
}
8298
8299
8.32k
inline void url_aggregator::update_base_search(std::string_view input) {
8300
8.32k
  ada_log("url_aggregator::update_base_search ", input);
8301
8.32k
  ADA_ASSERT_TRUE(validate());
8302
8.32k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8303
8.32k
  if (input.empty()) {
8304
0
    clear_search();
8305
0
    return;
8306
0
  }
8307
8308
8.32k
  if (input[0] == '?') {
8309
8.32k
    input.remove_prefix(1);
8310
8.32k
  }
8311
8312
8.32k
  if (components.hash_start == url_components::omitted) {
8313
8.32k
    if (components.search_start == url_components::omitted) {
8314
8.32k
      components.search_start = uint32_t(buffer.size());
8315
8.32k
      buffer += "?";
8316
8.32k
    } else {
8317
0
      buffer.resize(components.search_start + 1);
8318
0
    }
8319
8320
8.32k
    buffer.append(input);
8321
8.32k
  } else {
8322
0
    if (components.search_start == url_components::omitted) {
8323
0
      components.search_start = components.hash_start;
8324
0
    } else {
8325
0
      buffer.erase(components.search_start,
8326
0
                   components.hash_start - components.search_start);
8327
0
      components.hash_start = components.search_start;
8328
0
    }
8329
8330
0
    buffer.insert(components.search_start, "?");
8331
0
    buffer.insert(components.search_start + 1, input);
8332
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8333
0
  }
8334
8335
8.32k
  ADA_ASSERT_TRUE(validate());
8336
8.32k
}
8337
8338
inline void url_aggregator::update_base_search(
8339
72.4k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8340
72.4k
  ada_log("url_aggregator::update_base_search ", input,
8341
72.4k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8342
72.4k
  ADA_ASSERT_TRUE(validate());
8343
72.4k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8344
8345
72.4k
  if (components.hash_start == url_components::omitted) {
8346
71.3k
    if (components.search_start == url_components::omitted) {
8347
56.0k
      components.search_start = uint32_t(buffer.size());
8348
56.0k
      buffer += "?";
8349
56.0k
    } else {
8350
15.3k
      buffer.resize(components.search_start + 1);
8351
15.3k
    }
8352
8353
71.3k
    bool encoding_required =
8354
71.3k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8355
    // When encoding_required is false, then buffer is left unchanged, and
8356
    // percent encoding was not deemed required.
8357
71.3k
    if (!encoding_required) {
8358
52.4k
      buffer.append(input);
8359
52.4k
    }
8360
71.3k
  } else {
8361
1.04k
    if (components.search_start == url_components::omitted) {
8362
8
      components.search_start = components.hash_start;
8363
1.03k
    } else {
8364
1.03k
      buffer.erase(components.search_start,
8365
1.03k
                   components.hash_start - components.search_start);
8366
1.03k
      components.hash_start = components.search_start;
8367
1.03k
    }
8368
8369
1.04k
    buffer.insert(components.search_start, "?");
8370
1.04k
    size_t idx =
8371
1.04k
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8372
1.04k
    if (idx == input.size()) {
8373
956
      buffer.insert(components.search_start + 1, input);
8374
956
      components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8375
956
    } else {
8376
91
      buffer.insert(components.search_start + 1, input, 0, idx);
8377
91
      input.remove_prefix(idx);
8378
      // We only create a temporary string if we need percent encoding and
8379
      // we attempt to create as small a temporary string as we can.
8380
91
      std::string encoded =
8381
91
          ada::unicode::percent_encode(input, query_percent_encode_set);
8382
91
      buffer.insert(components.search_start + idx + 1, encoded);
8383
91
      components.hash_start +=
8384
91
          uint32_t(encoded.size() + idx + 1);  // Do not forget `?`
8385
91
    }
8386
1.04k
  }
8387
8388
72.4k
  ADA_ASSERT_TRUE(validate());
8389
72.4k
}
8390
8391
170k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8392
170k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8393
170k
          " bytes] \n", to_diagram());
8394
170k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8395
170k
  ADA_ASSERT_TRUE(validate());
8396
8397
170k
  const bool begins_with_dashdash = input.starts_with("//");
8398
170k
  if (!begins_with_dashdash && has_dash_dot()) {
8399
    // We must delete the ./
8400
30
    delete_dash_dot();
8401
30
  }
8402
8403
170k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8404
376
      !has_dash_dot()) {
8405
    // If url's host is null, url does not have an opaque path, url's path's
8406
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8407
    // output.
8408
332
    buffer.insert(components.pathname_start, "/.");
8409
332
    components.pathname_start += 2;
8410
332
    if (components.search_start != url_components::omitted) {
8411
0
      components.search_start += 2;
8412
0
    }
8413
332
    if (components.hash_start != url_components::omitted) {
8414
0
      components.hash_start += 2;
8415
0
    }
8416
332
  }
8417
8418
170k
  uint32_t difference = replace_and_resize(
8419
170k
      components.pathname_start,
8420
170k
      components.pathname_start + get_pathname_length(), input);
8421
170k
  if (components.search_start != url_components::omitted) {
8422
577
    components.search_start += difference;
8423
577
  }
8424
170k
  if (components.hash_start != url_components::omitted) {
8425
571
    components.hash_start += difference;
8426
571
  }
8427
170k
  ADA_ASSERT_TRUE(validate());
8428
170k
}
8429
8430
19
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8431
19
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8432
19
          "\n", to_diagram());
8433
19
  ADA_ASSERT_TRUE(validate());
8434
19
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8435
#if ADA_DEVELOPMENT_CHECKS
8436
  // computing the expected password.
8437
  std::string path_expected(get_pathname());
8438
  path_expected.append(input);
8439
#endif  // ADA_DEVELOPMENT_CHECKS
8440
19
  uint32_t ending_index = uint32_t(buffer.size());
8441
19
  if (components.search_start != url_components::omitted) {
8442
0
    ending_index = components.search_start;
8443
19
  } else if (components.hash_start != url_components::omitted) {
8444
0
    ending_index = components.hash_start;
8445
0
  }
8446
19
  buffer.insert(ending_index, input);
8447
8448
19
  if (components.search_start != url_components::omitted) {
8449
0
    components.search_start += uint32_t(input.size());
8450
0
  }
8451
19
  if (components.hash_start != url_components::omitted) {
8452
0
    components.hash_start += uint32_t(input.size());
8453
0
  }
8454
#if ADA_DEVELOPMENT_CHECKS
8455
  std::string path_after = std::string(get_pathname());
8456
  ADA_ASSERT_EQUAL(
8457
      path_expected, path_after,
8458
      "append_base_pathname problem after inserting " + std::string(input));
8459
#endif  // ADA_DEVELOPMENT_CHECKS
8460
19
  ADA_ASSERT_TRUE(validate());
8461
19
}
8462
8463
16.3k
inline void url_aggregator::update_base_username(const std::string_view input) {
8464
16.3k
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8465
16.3k
          "\n", to_diagram());
8466
16.3k
  ADA_ASSERT_TRUE(validate());
8467
16.3k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8468
8469
16.3k
  add_authority_slashes_if_needed();
8470
8471
16.3k
  bool has_password = has_non_empty_password();
8472
16.3k
  bool host_starts_with_at = buffer.size() > components.host_start &&
8473
16.3k
                             buffer[components.host_start] == '@';
8474
16.3k
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8475
16.3k
                                     components.username_end, input);
8476
8477
16.3k
  components.username_end += diff;
8478
16.3k
  components.host_start += diff;
8479
8480
16.3k
  if (!input.empty() && !host_starts_with_at) {
8481
12.1k
    buffer.insert(components.host_start, "@");
8482
12.1k
    diff++;
8483
12.1k
  } else if (input.empty() && host_starts_with_at && !has_password) {
8484
    // Input is empty, there is no password, and we need to remove "@" from
8485
    // hostname
8486
114
    buffer.erase(components.host_start, 1);
8487
114
    diff--;
8488
114
  }
8489
8490
16.3k
  components.host_end += diff;
8491
16.3k
  components.pathname_start += diff;
8492
16.3k
  if (components.search_start != url_components::omitted) {
8493
831
    components.search_start += diff;
8494
831
  }
8495
16.3k
  if (components.hash_start != url_components::omitted) {
8496
834
    components.hash_start += diff;
8497
834
  }
8498
16.3k
  ADA_ASSERT_TRUE(validate());
8499
16.3k
}
8500
8501
128k
inline void url_aggregator::append_base_username(const std::string_view input) {
8502
128k
  ada_log("url_aggregator::append_base_username ", input);
8503
128k
  ADA_ASSERT_TRUE(validate());
8504
128k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8505
#if ADA_DEVELOPMENT_CHECKS
8506
  // computing the expected password.
8507
  std::string username_expected(get_username());
8508
  username_expected.append(input);
8509
#endif  // ADA_DEVELOPMENT_CHECKS
8510
128k
  add_authority_slashes_if_needed();
8511
8512
  // If input is empty, do nothing.
8513
128k
  if (input.empty()) {
8514
36.2k
    return;
8515
36.2k
  }
8516
8517
92.4k
  uint32_t difference = uint32_t(input.size());
8518
92.4k
  buffer.insert(components.username_end, input);
8519
92.4k
  components.username_end += difference;
8520
92.4k
  components.host_start += difference;
8521
8522
92.4k
  if (buffer[components.host_start] != '@' &&
8523
49.8k
      components.host_start != components.host_end) {
8524
49.8k
    buffer.insert(components.host_start, "@");
8525
49.8k
    difference++;
8526
49.8k
  }
8527
8528
92.4k
  components.host_end += difference;
8529
92.4k
  components.pathname_start += difference;
8530
92.4k
  if (components.search_start != url_components::omitted) {
8531
0
    components.search_start += difference;
8532
0
  }
8533
92.4k
  if (components.hash_start != url_components::omitted) {
8534
0
    components.hash_start += difference;
8535
0
  }
8536
#if ADA_DEVELOPMENT_CHECKS
8537
  std::string username_after(get_username());
8538
  ADA_ASSERT_EQUAL(
8539
      username_expected, username_after,
8540
      "append_base_username problem after inserting " + std::string(input));
8541
#endif  // ADA_DEVELOPMENT_CHECKS
8542
92.4k
  ADA_ASSERT_TRUE(validate());
8543
92.4k
}
8544
8545
2.53k
constexpr void url_aggregator::clear_password() {
8546
2.53k
  ada_log("url_aggregator::clear_password ", to_string());
8547
2.53k
  ADA_ASSERT_TRUE(validate());
8548
2.53k
  if (!has_password()) {
8549
1.83k
    return;
8550
1.83k
  }
8551
8552
698
  uint32_t diff = components.host_start - components.username_end;
8553
698
  buffer.erase(components.username_end, diff);
8554
698
  components.host_start -= diff;
8555
698
  components.host_end -= diff;
8556
698
  components.pathname_start -= diff;
8557
698
  if (components.search_start != url_components::omitted) {
8558
678
    components.search_start -= diff;
8559
678
  }
8560
698
  if (components.hash_start != url_components::omitted) {
8561
678
    components.hash_start -= diff;
8562
678
  }
8563
698
}
8564
8565
15.8k
inline void url_aggregator::update_base_password(const std::string_view input) {
8566
15.8k
  ada_log("url_aggregator::update_base_password ", input);
8567
15.8k
  ADA_ASSERT_TRUE(validate());
8568
15.8k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8569
8570
15.8k
  add_authority_slashes_if_needed();
8571
8572
  // TODO: Optimization opportunity. Merge the following removal functions.
8573
15.8k
  if (input.empty()) {
8574
2.53k
    clear_password();
8575
8576
    // Remove username too, if it is empty.
8577
2.53k
    if (!has_non_empty_username()) {
8578
1.72k
      update_base_username("");
8579
1.72k
    }
8580
8581
2.53k
    return;
8582
2.53k
  }
8583
8584
13.2k
  bool password_exists = has_password();
8585
13.2k
  uint32_t difference = uint32_t(input.size());
8586
8587
13.2k
  if (password_exists) {
8588
1.01k
    uint32_t current_length =
8589
1.01k
        components.host_start - components.username_end - 1;
8590
1.01k
    buffer.erase(components.username_end + 1, current_length);
8591
1.01k
    difference -= current_length;
8592
12.2k
  } else {
8593
12.2k
    buffer.insert(components.username_end, ":");
8594
12.2k
    difference++;
8595
12.2k
  }
8596
8597
13.2k
  buffer.insert(components.username_end + 1, input);
8598
13.2k
  components.host_start += difference;
8599
8600
  // The following line is required to add "@" to hostname. When updating
8601
  // password if hostname does not start with "@", it is "update_base_password"s
8602
  // responsibility to set it.
8603
13.2k
  if (buffer[components.host_start] != '@') {
8604
29
    buffer.insert(components.host_start, "@");
8605
29
    difference++;
8606
29
  }
8607
8608
13.2k
  components.host_end += difference;
8609
13.2k
  components.pathname_start += difference;
8610
13.2k
  if (components.search_start != url_components::omitted) {
8611
1.12k
    components.search_start += difference;
8612
1.12k
  }
8613
13.2k
  if (components.hash_start != url_components::omitted) {
8614
1.12k
    components.hash_start += difference;
8615
1.12k
  }
8616
13.2k
  ADA_ASSERT_TRUE(validate());
8617
13.2k
}
8618
8619
86.1k
inline void url_aggregator::append_base_password(const std::string_view input) {
8620
86.1k
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8621
86.1k
          "\n", to_diagram());
8622
86.1k
  ADA_ASSERT_TRUE(validate());
8623
86.1k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8624
#if ADA_DEVELOPMENT_CHECKS
8625
  // computing the expected password.
8626
  std::string password_expected = std::string(get_password());
8627
  password_expected.append(input);
8628
#endif  // ADA_DEVELOPMENT_CHECKS
8629
86.1k
  add_authority_slashes_if_needed();
8630
8631
  // If input is empty, do nothing.
8632
86.1k
  if (input.empty()) {
8633
21.1k
    return;
8634
21.1k
  }
8635
8636
65.0k
  uint32_t difference = uint32_t(input.size());
8637
65.0k
  if (has_password()) {
8638
28.4k
    buffer.insert(components.host_start, input);
8639
36.6k
  } else {
8640
36.6k
    difference++;  // Increment for ":"
8641
36.6k
    buffer.insert(components.username_end, ":");
8642
36.6k
    buffer.insert(components.username_end + 1, input);
8643
36.6k
  }
8644
65.0k
  components.host_start += difference;
8645
8646
  // The following line is required to add "@" to hostname. When updating
8647
  // password if hostname does not start with "@", it is "append_base_password"s
8648
  // responsibility to set it.
8649
65.0k
  if (buffer[components.host_start] != '@') {
8650
7.45k
    buffer.insert(components.host_start, "@");
8651
7.45k
    difference++;
8652
7.45k
  }
8653
8654
65.0k
  components.host_end += difference;
8655
65.0k
  components.pathname_start += difference;
8656
65.0k
  if (components.search_start != url_components::omitted) {
8657
0
    components.search_start += difference;
8658
0
  }
8659
65.0k
  if (components.hash_start != url_components::omitted) {
8660
0
    components.hash_start += difference;
8661
0
  }
8662
#if ADA_DEVELOPMENT_CHECKS
8663
  std::string password_after(get_password());
8664
  ADA_ASSERT_EQUAL(
8665
      password_expected, password_after,
8666
      "append_base_password problem after inserting " + std::string(input));
8667
#endif  // ADA_DEVELOPMENT_CHECKS
8668
65.0k
  ADA_ASSERT_TRUE(validate());
8669
65.0k
}
8670
8671
51.6k
inline void url_aggregator::update_base_port(uint32_t input) {
8672
51.6k
  ada_log("url_aggregator::update_base_port");
8673
51.6k
  ADA_ASSERT_TRUE(validate());
8674
51.6k
  if (input == url_components::omitted) {
8675
11.7k
    clear_port();
8676
11.7k
    return;
8677
11.7k
  }
8678
  // calling std::to_string(input.value()) is unfortunate given that the port
8679
  // value is probably already available as a string.
8680
39.8k
  std::string value = helpers::concat(":", std::to_string(input));
8681
39.8k
  uint32_t difference = uint32_t(value.size());
8682
8683
39.8k
  if (components.port != url_components::omitted) {
8684
108
    difference -= components.pathname_start - components.host_end;
8685
108
    buffer.erase(components.host_end,
8686
108
                 components.pathname_start - components.host_end);
8687
108
  }
8688
8689
39.8k
  buffer.insert(components.host_end, value);
8690
39.8k
  components.pathname_start += difference;
8691
39.8k
  if (components.search_start != url_components::omitted) {
8692
1.10k
    components.search_start += difference;
8693
1.10k
  }
8694
39.8k
  if (components.hash_start != url_components::omitted) {
8695
1.10k
    components.hash_start += difference;
8696
1.10k
  }
8697
39.8k
  components.port = input;
8698
39.8k
  ADA_ASSERT_TRUE(validate());
8699
39.8k
}
8700
8701
38.7k
inline void url_aggregator::clear_port() {
8702
38.7k
  ada_log("url_aggregator::clear_port");
8703
38.7k
  ADA_ASSERT_TRUE(validate());
8704
38.7k
  if (components.port == url_components::omitted) {
8705
36.7k
    return;
8706
36.7k
  }
8707
2.04k
  uint32_t length = components.pathname_start - components.host_end;
8708
2.04k
  buffer.erase(components.host_end, length);
8709
2.04k
  components.pathname_start -= length;
8710
2.04k
  if (components.search_start != url_components::omitted) {
8711
2.04k
    components.search_start -= length;
8712
2.04k
  }
8713
2.04k
  if (components.hash_start != url_components::omitted) {
8714
2.04k
    components.hash_start -= length;
8715
2.04k
  }
8716
2.04k
  components.port = url_components::omitted;
8717
2.04k
  ADA_ASSERT_TRUE(validate());
8718
2.04k
}
8719
8720
11.9k
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8721
11.9k
  ada_log("url_aggregator::retrieve_base_port");
8722
11.9k
  return components.port;
8723
11.9k
}
8724
8725
24.7k
inline void url_aggregator::clear_search() {
8726
24.7k
  ada_log("url_aggregator::clear_search");
8727
24.7k
  ADA_ASSERT_TRUE(validate());
8728
24.7k
  if (components.search_start == url_components::omitted) {
8729
4.66k
    return;
8730
4.66k
  }
8731
8732
20.0k
  if (components.hash_start == url_components::omitted) {
8733
6.92k
    buffer.resize(components.search_start);
8734
13.1k
  } else {
8735
13.1k
    buffer.erase(components.search_start,
8736
13.1k
                 components.hash_start - components.search_start);
8737
13.1k
    components.hash_start = components.search_start;
8738
13.1k
  }
8739
8740
20.0k
  components.search_start = url_components::omitted;
8741
8742
#if ADA_DEVELOPMENT_CHECKS
8743
  ADA_ASSERT_EQUAL(get_search(), "",
8744
                   "search should have been cleared on buffer=" + buffer +
8745
                       " with " + components.to_string() + "\n" + to_diagram());
8746
#endif
8747
20.0k
  ADA_ASSERT_TRUE(validate());
8748
20.0k
}
8749
8750
14.6k
inline void url_aggregator::clear_hash() {
8751
14.6k
  ada_log("url_aggregator::clear_hash");
8752
14.6k
  ADA_ASSERT_TRUE(validate());
8753
14.6k
  if (components.hash_start == url_components::omitted) {
8754
1.67k
    return;
8755
1.67k
  }
8756
13.0k
  buffer.resize(components.hash_start);
8757
13.0k
  components.hash_start = url_components::omitted;
8758
8759
#if ADA_DEVELOPMENT_CHECKS
8760
  ADA_ASSERT_EQUAL(get_hash(), "",
8761
                   "hash should have been cleared on buffer=" + buffer +
8762
                       " with " + components.to_string() + "\n" + to_diagram());
8763
#endif
8764
13.0k
  ADA_ASSERT_TRUE(validate());
8765
13.0k
}
8766
8767
232k
constexpr void url_aggregator::clear_pathname() {
8768
232k
  ada_log("url_aggregator::clear_pathname");
8769
232k
  ADA_ASSERT_TRUE(validate());
8770
232k
  uint32_t ending_index = uint32_t(buffer.size());
8771
232k
  if (components.search_start != url_components::omitted) {
8772
577
    ending_index = components.search_start;
8773
232k
  } else if (components.hash_start != url_components::omitted) {
8774
8
    ending_index = components.hash_start;
8775
8
  }
8776
232k
  uint32_t pathname_length = ending_index - components.pathname_start;
8777
232k
  buffer.erase(components.pathname_start, pathname_length);
8778
232k
  uint32_t difference = pathname_length;
8779
232k
  if (components.pathname_start == components.host_end + 2 &&
8780
731
      buffer[components.host_end] == '/' &&
8781
0
      buffer[components.host_end + 1] == '.') {
8782
0
    components.pathname_start -= 2;
8783
0
    buffer.erase(components.host_end, 2);
8784
0
    difference += 2;
8785
0
  }
8786
232k
  if (components.search_start != url_components::omitted) {
8787
577
    components.search_start -= difference;
8788
577
  }
8789
232k
  if (components.hash_start != url_components::omitted) {
8790
571
    components.hash_start -= difference;
8791
571
  }
8792
232k
  ada_log("url_aggregator::clear_pathname completed, running checks...");
8793
#if ADA_DEVELOPMENT_CHECKS
8794
  ADA_ASSERT_EQUAL(get_pathname(), "",
8795
                   "pathname should have been cleared on buffer=" + buffer +
8796
                       " with " + components.to_string() + "\n" + to_diagram());
8797
#endif
8798
232k
  ADA_ASSERT_TRUE(validate());
8799
232k
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
8800
232k
}
8801
8802
81
constexpr void url_aggregator::clear_hostname() {
8803
81
  ada_log("url_aggregator::clear_hostname");
8804
81
  ADA_ASSERT_TRUE(validate());
8805
81
  if (!has_authority()) {
8806
0
    return;
8807
0
  }
8808
81
  ADA_ASSERT_TRUE(has_authority());
8809
8810
81
  uint32_t hostname_length = components.host_end - components.host_start;
8811
81
  uint32_t start = components.host_start;
8812
8813
  // If hostname starts with "@", we should not remove that character.
8814
81
  if (hostname_length > 0 && buffer[start] == '@') {
8815
0
    start++;
8816
0
    hostname_length--;
8817
0
  }
8818
81
  buffer.erase(start, hostname_length);
8819
81
  components.host_end = start;
8820
81
  components.pathname_start -= hostname_length;
8821
81
  if (components.search_start != url_components::omitted) {
8822
0
    components.search_start -= hostname_length;
8823
0
  }
8824
81
  if (components.hash_start != url_components::omitted) {
8825
0
    components.hash_start -= hostname_length;
8826
0
  }
8827
#if ADA_DEVELOPMENT_CHECKS
8828
  ADA_ASSERT_EQUAL(get_hostname(), "",
8829
                   "hostname should have been cleared on buffer=" + buffer +
8830
                       " with " + components.to_string() + "\n" + to_diagram());
8831
#endif
8832
81
  ADA_ASSERT_TRUE(has_authority());
8833
81
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
8834
81
                   "hostname should have been cleared on buffer=" + buffer +
8835
81
                       " with " + components.to_string() + "\n" + to_diagram());
8836
81
  ADA_ASSERT_TRUE(validate());
8837
81
}
8838
8839
149k
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
8840
149k
  ada_log("url_aggregator::has_hash");
8841
149k
  return components.hash_start != url_components::omitted;
8842
149k
}
8843
8844
162k
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
8845
162k
  ada_log("url_aggregator::has_search");
8846
162k
  return components.search_start != url_components::omitted;
8847
162k
}
8848
8849
75.2k
constexpr bool url_aggregator::has_credentials() const noexcept {
8850
75.2k
  ada_log("url_aggregator::has_credentials");
8851
75.2k
  return has_non_empty_username() || has_non_empty_password();
8852
75.2k
}
8853
8854
51.7k
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
8855
51.7k
  ada_log("url_aggregator::cannot_have_credentials_or_port");
8856
51.7k
  return type == ada::scheme::type::FILE ||
8857
49.0k
         components.host_start == components.host_end;
8858
51.7k
}
8859
8860
[[nodiscard]] ada_really_inline const ada::url_components&
8861
47.6k
url_aggregator::get_components() const noexcept {
8862
47.6k
  return components;
8863
47.6k
}
8864
8865
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
8866
1.00M
    const noexcept {
8867
1.00M
  ada_log("url_aggregator::has_authority");
8868
  // Performance: instead of doing this potentially expensive check, we could
8869
  // have a boolean in the struct.
8870
1.00M
  return components.protocol_end + 2 <= components.host_start &&
8871
428k
         buffer[components.protocol_end] == '/' &&
8872
428k
         buffer[components.protocol_end + 1] == '/';
8873
1.00M
}
8874
8875
874k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
8876
874k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
8877
874k
  ADA_ASSERT_TRUE(validate());
8878
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
8879
  // to insert
8880
  // `//` initially to the buffer, since it depends on the hostname existence.
8881
874k
  if (has_authority()) {
8882
311k
    return;
8883
311k
  }
8884
  // Performance: the common case is components.protocol_end == buffer.size()
8885
  // Optimization opportunity: in many cases, the "//" is part of the input and
8886
  // the insert could be fused with another insert.
8887
562k
  buffer.insert(components.protocol_end, "//");
8888
562k
  components.username_end += 2;
8889
562k
  components.host_start += 2;
8890
562k
  components.host_end += 2;
8891
562k
  components.pathname_start += 2;
8892
562k
  if (components.search_start != url_components::omitted) {
8893
0
    components.search_start += 2;
8894
0
  }
8895
562k
  if (components.hash_start != url_components::omitted) {
8896
0
    components.hash_start += 2;
8897
0
  }
8898
562k
  ADA_ASSERT_TRUE(validate());
8899
562k
}
8900
8901
1.11M
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
8902
1.11M
  buffer.reserve(capacity);
8903
1.11M
}
8904
8905
428k
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
8906
428k
  ada_log("url_aggregator::has_non_empty_username");
8907
428k
  return components.protocol_end + 2 < components.username_end;
8908
428k
}
8909
8910
415k
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
8911
415k
  ada_log("url_aggregator::has_non_empty_password");
8912
415k
  return components.host_start > components.username_end;
8913
415k
}
8914
8915
116k
constexpr bool url_aggregator::has_password() const noexcept {
8916
116k
  ada_log("url_aggregator::has_password");
8917
  // This function does not care about the length of the password
8918
116k
  return components.host_start > components.username_end &&
8919
42.8k
         buffer[components.username_end] == ':';
8920
116k
}
8921
8922
35.7k
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
8923
35.7k
  if (!has_hostname()) {
8924
3.84k
    return false;
8925
3.84k
  }
8926
31.8k
  if (components.host_start == components.host_end) {
8927
1.47k
    return true;
8928
1.47k
  }
8929
30.4k
  if (components.host_end > components.host_start + 1) {
8930
29.1k
    return false;
8931
29.1k
  }
8932
1.21k
  return components.username_end != components.host_start;
8933
30.4k
}
8934
8935
123k
constexpr bool url_aggregator::has_hostname() const noexcept {
8936
123k
  return has_authority();
8937
123k
}
8938
8939
50.4k
constexpr bool url_aggregator::has_port() const noexcept {
8940
50.4k
  ada_log("url_aggregator::has_port");
8941
  // A URL cannot have a username/password/port if its host is null or the empty
8942
  // string, or its scheme is "file".
8943
50.4k
  return has_hostname() && components.pathname_start != components.host_end;
8944
50.4k
}
8945
8946
179k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
8947
  // If url's host is null, url does not have an opaque path, url's path's size
8948
  // is greater than 1, and url's path[0] is the empty string, then append
8949
  // U+002F (/) followed by U+002E (.) to output.
8950
179k
  ada_log("url_aggregator::has_dash_dot");
8951
#if ADA_DEVELOPMENT_CHECKS
8952
  // If pathname_start and host_end are exactly two characters apart, then we
8953
  // either have a one-digit port such as http://test.com:5?param=1 or else we
8954
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
8955
  if (components.pathname_start == components.host_end + 2) {
8956
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
8957
                     buffer[components.host_end + 1] == '.') ||
8958
                    (buffer[components.host_end] == ':' &&
8959
                     checkers::is_digit(buffer[components.host_end + 1])));
8960
  }
8961
  if (components.pathname_start == components.host_end + 2 &&
8962
      buffer[components.host_end] == '/' &&
8963
      buffer[components.host_end + 1] == '.') {
8964
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
8965
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
8966
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
8967
  }
8968
#endif
8969
  // Performance: it should be uncommon for components.pathname_start ==
8970
  // components.host_end + 2 to be true. So we put this check first in the
8971
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
8972
  // more expensive, but should be uncommon.
8973
179k
  return components.pathname_start == components.host_end + 2 &&
8974
3.55k
         !has_opaque_path && buffer[components.host_end] == '/' &&
8975
74
         buffer[components.host_end + 1] == '.';
8976
179k
}
8977
8978
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
8979
543k
    const noexcept ada_lifetime_bound {
8980
543k
  ada_log("url_aggregator::get_href");
8981
543k
  return buffer;
8982
543k
}
8983
8984
142k
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
8985
142k
  return buffer.size();
8986
142k
}
8987
8988
ada_really_inline size_t
8989
54.4k
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
8990
54.4k
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
8991
54.4k
  if (!view.empty() && view[0] == '-') {
8992
189
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
8993
189
    is_valid = false;
8994
189
    return 0;
8995
189
  }
8996
54.2k
  uint16_t parsed_port{};
8997
54.2k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
8998
54.2k
  if (r.ec == std::errc::result_out_of_range) {
8999
1.18k
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9000
1.18k
    is_valid = false;
9001
1.18k
    return 0;
9002
1.18k
  }
9003
53.0k
  ada_log("parse_port: ", parsed_port);
9004
53.0k
  const size_t consumed = size_t(r.ptr - view.data());
9005
53.0k
  ada_log("parse_port: consumed ", consumed);
9006
53.0k
  if (check_trailing_content) {
9007
51.0k
    is_valid &=
9008
51.0k
        (consumed == view.size() || view[consumed] == '/' ||
9009
4.08k
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9010
51.0k
  }
9011
53.0k
  ada_log("parse_port: is_valid = ", is_valid);
9012
53.0k
  if (is_valid) {
9013
50.1k
    ada_log("parse_port", r.ec == std::errc());
9014
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9015
50.1k
    auto default_port = scheme_default_port();
9016
50.1k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9017
49.9k
                         (default_port != parsed_port);
9018
50.1k
    if (r.ec == std::errc() && is_port_valid) {
9019
39.6k
      update_base_port(parsed_port);
9020
39.6k
    } else {
9021
10.4k
      clear_port();
9022
10.4k
    }
9023
50.1k
  }
9024
53.0k
  return consumed;
9025
54.2k
}
9026
9027
7.44k
constexpr void url_aggregator::set_protocol_as_file() {
9028
7.44k
  ada_log("url_aggregator::set_protocol_as_file ");
9029
7.44k
  ADA_ASSERT_TRUE(validate());
9030
7.44k
  type = ada::scheme::type::FILE;
9031
  // next line could overflow but unsigned arithmetic has well-defined
9032
  // overflows.
9033
7.44k
  uint32_t new_difference = 5 - components.protocol_end;
9034
9035
7.44k
  if (buffer.empty()) {
9036
1.74k
    buffer.append("file:");
9037
5.69k
  } else {
9038
5.69k
    buffer.erase(0, components.protocol_end);
9039
5.69k
    buffer.insert(0, "file:");
9040
5.69k
  }
9041
7.44k
  components.protocol_end = 5;
9042
9043
  // Update the rest of the components.
9044
7.44k
  components.username_end += new_difference;
9045
7.44k
  components.host_start += new_difference;
9046
7.44k
  components.host_end += new_difference;
9047
7.44k
  components.pathname_start += new_difference;
9048
7.44k
  if (components.search_start != url_components::omitted) {
9049
0
    components.search_start += new_difference;
9050
0
  }
9051
7.44k
  if (components.hash_start != url_components::omitted) {
9052
0
    components.hash_start += new_difference;
9053
0
  }
9054
7.44k
  ADA_ASSERT_TRUE(validate());
9055
7.44k
}
9056
9057
148k
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9058
148k
  if (!is_valid) {
9059
0
    return true;
9060
0
  }
9061
148k
  if (!components.check_offset_consistency()) {
9062
0
    ada_log("url_aggregator::validate inconsistent components \n",
9063
0
            to_diagram());
9064
0
    return false;
9065
0
  }
9066
  // We have a credible components struct, but let us investivate more
9067
  // carefully:
9068
  /**
9069
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9070
   *       |     |    |          | ^^^^|       |   |
9071
   *       |     |    |          | |   |       |   `----- hash_start
9072
   *       |     |    |          | |   |       `--------- search_start
9073
   *       |     |    |          | |   `----------------- pathname_start
9074
   *       |     |    |          | `--------------------- port
9075
   *       |     |    |          `----------------------- host_end
9076
   *       |     |    `---------------------------------- host_start
9077
   *       |     `--------------------------------------- username_end
9078
   *       `--------------------------------------------- protocol_end
9079
   */
9080
148k
  if (components.protocol_end == url_components::omitted) {
9081
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9082
0
    return false;
9083
0
  }
9084
148k
  if (components.username_end == url_components::omitted) {
9085
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9086
0
    return false;
9087
0
  }
9088
148k
  if (components.host_start == url_components::omitted) {
9089
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9090
0
    return false;
9091
0
  }
9092
148k
  if (components.host_end == url_components::omitted) {
9093
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9094
0
    return false;
9095
0
  }
9096
148k
  if (components.pathname_start == url_components::omitted) {
9097
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9098
0
    return false;
9099
0
  }
9100
9101
148k
  if (components.protocol_end > buffer.size()) {
9102
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9103
0
    return false;
9104
0
  }
9105
148k
  if (components.username_end > buffer.size()) {
9106
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9107
0
    return false;
9108
0
  }
9109
148k
  if (components.host_start > buffer.size()) {
9110
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9111
0
    return false;
9112
0
  }
9113
148k
  if (components.host_end > buffer.size()) {
9114
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9115
0
    return false;
9116
0
  }
9117
148k
  if (components.pathname_start > buffer.size()) {
9118
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9119
0
            to_diagram());
9120
0
    return false;
9121
0
  }
9122
9123
148k
  if (components.protocol_end > 0) {
9124
148k
    if (buffer[components.protocol_end - 1] != ':') {
9125
0
      ada_log(
9126
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9127
0
          to_diagram());
9128
0
      return false;
9129
0
    }
9130
148k
  }
9131
9132
148k
  if (components.username_end != buffer.size() &&
9133
147k
      components.username_end > components.protocol_end + 2) {
9134
42.1k
    if (buffer[components.username_end] != ':' &&
9135
3.46k
        buffer[components.username_end] != '@') {
9136
0
      ada_log(
9137
0
          "url_aggregator::validate missing : or @ at the end of the username "
9138
0
          "\n",
9139
0
          to_diagram());
9140
0
      return false;
9141
0
    }
9142
42.1k
  }
9143
9144
148k
  if (components.host_start != buffer.size()) {
9145
147k
    if (components.host_start > components.username_end) {
9146
39.6k
      if (buffer[components.host_start] != '@') {
9147
0
        ada_log(
9148
0
            "url_aggregator::validate missing @ at the end of the password \n",
9149
0
            to_diagram());
9150
0
        return false;
9151
0
      }
9152
107k
    } else if (components.host_start == components.username_end &&
9153
107k
               components.host_end > components.host_start) {
9154
100k
      if (components.host_start == components.protocol_end + 2) {
9155
96.9k
        if (buffer[components.protocol_end] != '/' ||
9156
96.9k
            buffer[components.protocol_end + 1] != '/') {
9157
0
          ada_log(
9158
0
              "url_aggregator::validate missing // between protocol and host "
9159
0
              "\n",
9160
0
              to_diagram());
9161
0
          return false;
9162
0
        }
9163
96.9k
      } else {
9164
3.46k
        if (components.host_start > components.protocol_end &&
9165
3.46k
            buffer[components.host_start] != '@') {
9166
0
          ada_log(
9167
0
              "url_aggregator::validate missing @ at the end of the username "
9168
0
              "\n",
9169
0
              to_diagram());
9170
0
          return false;
9171
0
        }
9172
3.46k
      }
9173
100k
    } else {
9174
7.61k
      if (components.host_end != components.host_start) {
9175
0
        ada_log("url_aggregator::validate expected omitted host \n",
9176
0
                to_diagram());
9177
0
        return false;
9178
0
      }
9179
7.61k
    }
9180
147k
  }
9181
148k
  if (components.host_end != buffer.size() &&
9182
147k
      components.pathname_start > components.host_end) {
9183
31.9k
    if (components.pathname_start == components.host_end + 2 &&
9184
2.65k
        buffer[components.host_end] == '/' &&
9185
163
        buffer[components.host_end + 1] == '.') {
9186
163
      if (components.pathname_start + 1 >= buffer.size() ||
9187
163
          buffer[components.pathname_start] != '/' ||
9188
163
          buffer[components.pathname_start + 1] != '/') {
9189
0
        ada_log(
9190
0
            "url_aggregator::validate expected the path to begin with // \n",
9191
0
            to_diagram());
9192
0
        return false;
9193
0
      }
9194
31.8k
    } else if (buffer[components.host_end] != ':') {
9195
0
      ada_log("url_aggregator::validate missing : at the port \n",
9196
0
              to_diagram());
9197
0
      return false;
9198
0
    }
9199
31.9k
  }
9200
148k
  if (components.pathname_start != buffer.size() &&
9201
146k
      components.pathname_start < components.search_start &&
9202
146k
      components.pathname_start < components.hash_start && !has_opaque_path) {
9203
143k
    if (buffer[components.pathname_start] != '/') {
9204
0
      ada_log("url_aggregator::validate missing / at the path \n",
9205
0
              to_diagram());
9206
0
      return false;
9207
0
    }
9208
143k
  }
9209
148k
  if (components.search_start != url_components::omitted) {
9210
71.1k
    if (buffer[components.search_start] != '?') {
9211
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9212
0
              to_diagram());
9213
0
      return false;
9214
0
    }
9215
71.1k
  }
9216
148k
  if (components.hash_start != url_components::omitted) {
9217
49.7k
    if (buffer[components.hash_start] != '#') {
9218
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9219
0
              to_diagram());
9220
0
      return false;
9221
0
    }
9222
49.7k
  }
9223
9224
148k
  return true;
9225
148k
}
9226
9227
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9228
932k
    ada_lifetime_bound {
9229
932k
  ada_log("url_aggregator::get_pathname pathname_start = ",
9230
932k
          components.pathname_start, " buffer.size() = ", buffer.size(),
9231
932k
          " components.search_start = ", components.search_start,
9232
932k
          " components.hash_start = ", components.hash_start);
9233
932k
  auto ending_index = uint32_t(buffer.size());
9234
932k
  if (components.search_start != url_components::omitted) {
9235
66.4k
    ending_index = components.search_start;
9236
865k
  } else if (components.hash_start != url_components::omitted) {
9237
13.6k
    ending_index = components.hash_start;
9238
13.6k
  }
9239
932k
  return helpers::substring(buffer, components.pathname_start, ending_index);
9240
932k
}
9241
9242
inline std::ostream& operator<<(std::ostream& out,
9243
0
                                const ada::url_aggregator& u) {
9244
0
  return out << u.to_string();
9245
0
}
9246
9247
13.6k
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9248
13.6k
  ada_log("url_aggregator::update_host_to_base_host ", input);
9249
13.6k
  ADA_ASSERT_TRUE(validate());
9250
13.6k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9251
13.6k
  if (type != ada::scheme::type::FILE) {
9252
    // Let host be the result of host parsing host_view with url is not special.
9253
11.9k
    if (input.empty() && !is_special()) {
9254
1.15k
      if (has_hostname()) {
9255
81
        clear_hostname();
9256
1.07k
      } else if (has_dash_dot()) {
9257
0
        add_authority_slashes_if_needed();
9258
0
        delete_dash_dot();
9259
0
      }
9260
1.15k
      return;
9261
1.15k
    }
9262
11.9k
  }
9263
12.4k
  update_base_hostname(input);
9264
12.4k
  ADA_ASSERT_TRUE(validate());
9265
12.4k
  return;
9266
13.6k
}
9267
}  // namespace ada
9268
9269
#endif  // ADA_URL_AGGREGATOR_INL_H
9270
/* end file include/ada/url_aggregator-inl.h */
9271
/* begin file include/ada/url_search_params.h */
9272
/**
9273
 * @file url_search_params.h
9274
 * @brief URL query string parameter manipulation.
9275
 *
9276
 * This file provides the `url_search_params` class for parsing, manipulating,
9277
 * and serializing URL query strings. It implements the URLSearchParams API
9278
 * from the WHATWG URL Standard.
9279
 *
9280
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9281
 */
9282
#ifndef ADA_URL_SEARCH_PARAMS_H
9283
#define ADA_URL_SEARCH_PARAMS_H
9284
9285
#include <optional>
9286
#include <string>
9287
#include <string_view>
9288
#include <vector>
9289
9290
namespace ada {
9291
9292
/**
9293
 * @brief Iterator types for url_search_params iteration.
9294
 */
9295
enum class url_search_params_iter_type {
9296
  KEYS,    /**< Iterate over parameter keys only */
9297
  VALUES,  /**< Iterate over parameter values only */
9298
  ENTRIES, /**< Iterate over key-value pairs */
9299
};
9300
9301
template <typename T, url_search_params_iter_type Type>
9302
struct url_search_params_iter;
9303
9304
/** Type alias for a key-value pair of string views. */
9305
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9306
9307
/** Iterator over search parameter keys. */
9308
using url_search_params_keys_iter =
9309
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9310
/** Iterator over search parameter values. */
9311
using url_search_params_values_iter =
9312
    url_search_params_iter<std::string_view,
9313
                           url_search_params_iter_type::VALUES>;
9314
/** Iterator over search parameter key-value pairs. */
9315
using url_search_params_entries_iter =
9316
    url_search_params_iter<key_value_view_pair,
9317
                           url_search_params_iter_type::ENTRIES>;
9318
9319
/**
9320
 * @brief Class for parsing and manipulating URL query strings.
9321
 *
9322
 * The `url_search_params` class provides methods to parse, modify, and
9323
 * serialize URL query parameters (the part after '?' in a URL). It handles
9324
 * percent-encoding and decoding automatically.
9325
 *
9326
 * All string inputs must be valid UTF-8. The caller is responsible for
9327
 * ensuring UTF-8 validity.
9328
 *
9329
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9330
 */
9331
struct url_search_params {
9332
7.75k
  url_search_params() = default;
9333
9334
  /**
9335
   * Constructs url_search_params by parsing a query string.
9336
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9337
   */
9338
42.2k
  explicit url_search_params(const std::string_view input) {
9339
42.2k
    initialize(input);
9340
42.2k
  }
9341
9342
2.58k
  url_search_params(const url_search_params& u) = default;
9343
2.58k
  url_search_params(url_search_params&& u) noexcept = default;
9344
2.58k
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9345
2.58k
  url_search_params& operator=(const url_search_params& u) = default;
9346
55.2k
  ~url_search_params() = default;
9347
9348
  /**
9349
   * Returns the number of key-value pairs.
9350
   * @return The total count of parameters.
9351
   */
9352
  [[nodiscard]] inline size_t size() const noexcept;
9353
9354
  /**
9355
   * Appends a new key-value pair to the parameter list.
9356
   * @param key The parameter name (must be valid UTF-8).
9357
   * @param value The parameter value (must be valid UTF-8).
9358
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9359
   */
9360
  inline void append(std::string_view key, std::string_view value);
9361
9362
  /**
9363
   * Removes all pairs with the given key.
9364
   * @param key The parameter name to remove.
9365
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9366
   */
9367
  inline void remove(std::string_view key);
9368
9369
  /**
9370
   * Removes all pairs with the given key and value.
9371
   * @param key The parameter name.
9372
   * @param value The parameter value to match.
9373
   */
9374
  inline void remove(std::string_view key, std::string_view value);
9375
9376
  /**
9377
   * Returns the value of the first pair with the given key.
9378
   * @param key The parameter name to search for.
9379
   * @return The value if found, or std::nullopt if not present.
9380
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9381
   */
9382
  inline std::optional<std::string_view> get(std::string_view key);
9383
9384
  /**
9385
   * Returns all values for pairs with the given key.
9386
   * @param key The parameter name to search for.
9387
   * @return A vector of all matching values (may be empty).
9388
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9389
   */
9390
  inline std::vector<std::string> get_all(std::string_view key);
9391
9392
  /**
9393
   * Checks if any pair has the given key.
9394
   * @param key The parameter name to search for.
9395
   * @return `true` if at least one pair has this key.
9396
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9397
   */
9398
  inline bool has(std::string_view key) noexcept;
9399
9400
  /**
9401
   * Checks if any pair matches the given key and value.
9402
   * @param key The parameter name to search for.
9403
   * @param value The parameter value to match.
9404
   * @return `true` if a matching pair exists.
9405
   */
9406
  inline bool has(std::string_view key, std::string_view value) noexcept;
9407
9408
  /**
9409
   * Sets a parameter value, replacing any existing pairs with the same key.
9410
   * @param key The parameter name (must be valid UTF-8).
9411
   * @param value The parameter value (must be valid UTF-8).
9412
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9413
   */
9414
  inline void set(std::string_view key, std::string_view value);
9415
9416
  /**
9417
   * Sorts all key-value pairs by their keys using code unit comparison.
9418
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9419
   */
9420
  inline void sort();
9421
9422
  /**
9423
   * Serializes the parameters to a query string (without leading '?').
9424
   * @return The percent-encoded query string.
9425
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9426
   */
9427
  inline std::string to_string() const;
9428
9429
  /**
9430
   * Returns an iterator over all parameter keys.
9431
   * Keys may repeat if there are duplicate parameters.
9432
   * @return An iterator yielding string_view keys.
9433
   * @note The iterator is invalidated if this object is modified.
9434
   */
9435
  inline url_search_params_keys_iter get_keys();
9436
9437
  /**
9438
   * Returns an iterator over all parameter values.
9439
   * @return An iterator yielding string_view values.
9440
   * @note The iterator is invalidated if this object is modified.
9441
   */
9442
  inline url_search_params_values_iter get_values();
9443
9444
  /**
9445
   * Returns an iterator over all key-value pairs.
9446
   * @return An iterator yielding key-value pair views.
9447
   * @note The iterator is invalidated if this object is modified.
9448
   */
9449
  inline url_search_params_entries_iter get_entries();
9450
9451
  /**
9452
   * C++ style conventional iterator support. const only because we
9453
   * do not really want the params to be modified via the iterator.
9454
   */
9455
10.3k
  inline auto begin() const { return params.begin(); }
9456
10.3k
  inline auto end() const { return params.end(); }
9457
1.08k
  inline auto front() const { return params.front(); }
9458
1.08k
  inline auto back() const { return params.back(); }
9459
1.08k
  inline auto operator[](size_t index) const { return params[index]; }
9460
9461
  /**
9462
   * @private
9463
   * Used to reset the search params to a new input.
9464
   * Used primarily for C API.
9465
   * @param input
9466
   */
9467
  void reset(std::string_view input);
9468
9469
 private:
9470
  typedef std::pair<std::string, std::string> key_value_pair;
9471
  std::vector<key_value_pair> params{};
9472
9473
  /**
9474
   * The init parameter must be valid UTF-8.
9475
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9476
   */
9477
  void initialize(std::string_view init);
9478
9479
  template <typename T, url_search_params_iter_type Type>
9480
  friend struct url_search_params_iter;
9481
};  // url_search_params
9482
9483
/**
9484
 * @brief JavaScript-style iterator for url_search_params.
9485
 *
9486
 * Provides a `next()` method that returns successive values until exhausted.
9487
 * This matches the iterator pattern used in the Web Platform.
9488
 *
9489
 * @tparam T The type of value returned by the iterator.
9490
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9491
 *
9492
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9493
 */
9494
template <typename T, url_search_params_iter_type Type>
9495
struct url_search_params_iter {
9496
0
  inline url_search_params_iter() : params(EMPTY) {}
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter()
Unexecuted instantiation: ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter()
Unexecuted instantiation: ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter()
9497
  url_search_params_iter(const url_search_params_iter& u) = default;
9498
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9499
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9500
      default;
9501
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9502
  ~url_search_params_iter() = default;
9503
9504
  /**
9505
   * Returns the next value in the iteration sequence.
9506
   * @return The next value, or std::nullopt if iteration is complete.
9507
   */
9508
  inline std::optional<T> next();
9509
9510
  /**
9511
   * Checks if more values are available.
9512
   * @return `true` if `next()` will return a value, `false` if exhausted.
9513
   */
9514
  inline bool has_next() const;
9515
9516
 private:
9517
  static url_search_params EMPTY;
9518
7.75k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::url_search_params_iter(ada::url_search_params&)
Line
Count
Source
9518
2.58k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::url_search_params_iter(ada::url_search_params&)
Line
Count
Source
9518
2.58k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::url_search_params_iter(ada::url_search_params&)
Line
Count
Source
9518
2.58k
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
9519
9520
  url_search_params& params;
9521
  size_t pos = 0;
9522
9523
  friend struct url_search_params;
9524
};
9525
9526
}  // namespace ada
9527
#endif
9528
/* end file include/ada/url_search_params.h */
9529
/* begin file include/ada/url_search_params-inl.h */
9530
/**
9531
 * @file url_search_params-inl.h
9532
 * @brief Inline declarations for the URL Search Params
9533
 */
9534
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9535
#define ADA_URL_SEARCH_PARAMS_INL_H
9536
9537
9538
#include <algorithm>
9539
#include <optional>
9540
#include <ranges>
9541
#include <string>
9542
#include <string_view>
9543
#include <vector>
9544
9545
namespace ada {
9546
9547
// A default, empty url_search_params for use with empty iterators.
9548
template <typename T, ada::url_search_params_iter_type Type>
9549
url_search_params url_search_params_iter<T, Type>::EMPTY;
9550
9551
2.58k
inline void url_search_params::reset(std::string_view input) {
9552
2.58k
  params.clear();
9553
2.58k
  initialize(input);
9554
2.58k
}
9555
9556
44.8k
inline void url_search_params::initialize(std::string_view input) {
9557
44.8k
  if (!input.empty() && input.front() == '?') {
9558
105
    input.remove_prefix(1);
9559
105
  }
9560
44.8k
  if (input.empty()) {
9561
11.9k
    return;
9562
11.9k
  }
9563
9564
32.9k
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9565
9566
78.3k
  auto process_key_value = [&](const std::string_view current) {
9567
78.3k
    const auto equal = current.find('=');
9568
78.3k
    if (equal == std::string_view::npos) {
9569
25.5k
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9570
52.8k
    } else {
9571
52.8k
      params.emplace_back(
9572
52.8k
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9573
52.8k
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9574
52.8k
    }
9575
78.3k
  };
9576
9577
81.5k
  while (!input.empty()) {
9578
81.3k
    const auto ampersand_index = input.find('&');
9579
9580
81.3k
    if (ampersand_index == std::string_view::npos) {
9581
32.7k
      if (!input.empty()) {
9582
32.7k
        process_key_value(input);
9583
32.7k
      }
9584
32.7k
      break;
9585
48.6k
    } else if (ampersand_index != 0) {
9586
45.6k
      process_key_value(input.substr(0, ampersand_index));
9587
45.6k
    }
9588
9589
48.6k
    input.remove_prefix(ampersand_index + 1);
9590
48.6k
  }
9591
32.9k
}
9592
9593
inline void url_search_params::append(const std::string_view key,
9594
29.0k
                                      const std::string_view value) {
9595
29.0k
  params.emplace_back(key, value);
9596
29.0k
}
9597
9598
23.2k
inline size_t url_search_params::size() const noexcept { return params.size(); }
9599
9600
inline std::optional<std::string_view> url_search_params::get(
9601
17.6k
    const std::string_view key) {
9602
17.6k
  auto entry = std::ranges::find_if(
9603
133k
      params, [&key](const auto& param) { return param.first == key; });
9604
9605
17.6k
  if (entry == params.end()) {
9606
4.85k
    return std::nullopt;
9607
4.85k
  }
9608
9609
12.8k
  return entry->second;
9610
17.6k
}
9611
9612
inline std::vector<std::string> url_search_params::get_all(
9613
5.17k
    const std::string_view key) {
9614
5.17k
  std::vector<std::string> out{};
9615
9616
15.7k
  for (auto& param : params) {
9617
15.7k
    if (param.first == key) {
9618
6.45k
      out.emplace_back(param.second);
9619
6.45k
    }
9620
15.7k
  }
9621
9622
5.17k
  return out;
9623
5.17k
}
9624
9625
22.3k
inline bool url_search_params::has(const std::string_view key) noexcept {
9626
22.3k
  auto entry = std::ranges::find_if(
9627
250k
      params, [&key](const auto& param) { return param.first == key; });
9628
22.3k
  return entry != params.end();
9629
22.3k
}
9630
9631
inline bool url_search_params::has(std::string_view key,
9632
12.4k
                                   std::string_view value) noexcept {
9633
129k
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9634
129k
    return param.first == key && param.second == value;
9635
129k
  });
9636
12.4k
  return entry != params.end();
9637
12.4k
}
9638
9639
52.6k
inline std::string url_search_params::to_string() const {
9640
52.6k
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9641
52.6k
  std::string out{};
9642
168k
  for (size_t i = 0; i < params.size(); i++) {
9643
115k
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9644
115k
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9645
9646
    // Performance optimization: Move this inside percent_encode.
9647
115k
    std::ranges::replace(key, ' ', '+');
9648
115k
    std::ranges::replace(value, ' ', '+');
9649
9650
115k
    if (i != 0) {
9651
74.7k
      out += "&";
9652
74.7k
    }
9653
115k
    out.append(key);
9654
115k
    out += "=";
9655
115k
    out.append(value);
9656
115k
  }
9657
52.6k
  return out;
9658
52.6k
}
9659
9660
inline void url_search_params::set(const std::string_view key,
9661
2.58k
                                   const std::string_view value) {
9662
5.17k
  const auto find = [&key](const auto& param) { return param.first == key; };
9663
9664
2.58k
  auto it = std::ranges::find_if(params, find);
9665
9666
2.58k
  if (it == params.end()) {
9667
0
    params.emplace_back(key, value);
9668
2.58k
  } else {
9669
2.58k
    it->second = value;
9670
2.58k
    params.erase(std::remove_if(std::next(it), params.end(), find),
9671
2.58k
                 params.end());
9672
2.58k
  }
9673
2.58k
}
9674
9675
3.58k
inline void url_search_params::remove(const std::string_view key) {
9676
3.58k
  std::erase_if(params,
9677
7.83k
                [&key](const auto& param) { return param.first == key; });
9678
3.58k
}
9679
9680
inline void url_search_params::remove(const std::string_view key,
9681
3.58k
                                      const std::string_view value) {
9682
3.58k
  std::erase_if(params, [&key, &value](const auto& param) {
9683
2.83k
    return param.first == key && param.second == value;
9684
2.83k
  });
9685
3.58k
}
9686
9687
5.17k
inline void url_search_params::sort() {
9688
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9689
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9690
5.17k
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9691
39.5k
                                      const key_value_pair& rhs) {
9692
39.5k
    size_t i = 0, j = 0;
9693
39.5k
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9694
61.3k
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9695
38.0k
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9696
36.1k
      uint32_t codePoint1 = 0, codePoint2 = 0;
9697
9698
36.1k
      if (low_surrogate1 != 0) {
9699
987
        codePoint1 = low_surrogate1;
9700
987
        low_surrogate1 = 0;
9701
35.2k
      } else {
9702
35.2k
        uint8_t c1 = uint8_t(lhs.first[i]);
9703
35.2k
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9704
2.00k
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9705
2.00k
          i += 2;
9706
33.2k
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9707
1.08k
          codePoint1 = ((c1 & 0x0F) << 12) |
9708
1.08k
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9709
1.08k
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9710
1.08k
          i += 3;
9711
32.1k
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9712
1.26k
          codePoint1 = ((c1 & 0x07) << 18) |
9713
1.26k
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9714
1.26k
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9715
1.26k
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9716
1.26k
          i += 4;
9717
9718
1.26k
          codePoint1 -= 0x10000;
9719
1.26k
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9720
1.26k
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9721
1.26k
          codePoint1 = high_surrogate;
9722
30.8k
        } else {
9723
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9724
30.8k
          codePoint1 = c1;
9725
30.8k
          i++;
9726
30.8k
        }
9727
35.2k
      }
9728
9729
36.1k
      if (low_surrogate2 != 0) {
9730
989
        codePoint2 = low_surrogate2;
9731
989
        low_surrogate2 = 0;
9732
35.2k
      } else {
9733
35.2k
        uint8_t c2 = uint8_t(rhs.first[j]);
9734
35.2k
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
9735
2.08k
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
9736
2.08k
          j += 2;
9737
33.1k
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
9738
1.10k
          codePoint2 = ((c2 & 0x0F) << 12) |
9739
1.10k
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
9740
1.10k
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
9741
1.10k
          j += 3;
9742
32.0k
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
9743
1.39k
          codePoint2 = ((c2 & 0x07) << 18) |
9744
1.39k
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
9745
1.39k
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
9746
1.39k
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
9747
1.39k
          j += 4;
9748
1.39k
          codePoint2 -= 0x10000;
9749
1.39k
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
9750
1.39k
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
9751
1.39k
          codePoint2 = high_surrogate;
9752
30.6k
        } else {
9753
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
9754
30.6k
          codePoint2 = c2;
9755
30.6k
          j++;
9756
30.6k
        }
9757
35.2k
      }
9758
9759
36.1k
      if (codePoint1 != codePoint2) {
9760
14.4k
        return (codePoint1 < codePoint2);
9761
14.4k
      }
9762
36.1k
    }
9763
25.1k
    return (j < rhs.first.size() || low_surrogate2 != 0);
9764
39.5k
  });
9765
5.17k
}
9766
9767
2.58k
inline url_search_params_keys_iter url_search_params::get_keys() {
9768
2.58k
  return url_search_params_keys_iter(*this);
9769
2.58k
}
9770
9771
/**
9772
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9773
 */
9774
2.58k
inline url_search_params_values_iter url_search_params::get_values() {
9775
2.58k
  return url_search_params_values_iter(*this);
9776
2.58k
}
9777
9778
/**
9779
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9780
 */
9781
2.58k
inline url_search_params_entries_iter url_search_params::get_entries() {
9782
2.58k
  return url_search_params_entries_iter(*this);
9783
2.58k
}
9784
9785
template <typename T, url_search_params_iter_type Type>
9786
59.2k
inline bool url_search_params_iter<T, Type>::has_next() const {
9787
59.2k
  return pos < params.params.size();
9788
59.2k
}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>::has_next() const
Line
Count
Source
9786
19.7k
inline bool url_search_params_iter<T, Type>::has_next() const {
9787
19.7k
  return pos < params.params.size();
9788
19.7k
}
ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>::has_next() const
Line
Count
Source
9786
19.7k
inline bool url_search_params_iter<T, Type>::has_next() const {
9787
19.7k
  return pos < params.params.size();
9788
19.7k
}
ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>::has_next() const
Line
Count
Source
9786
19.7k
inline bool url_search_params_iter<T, Type>::has_next() const {
9787
19.7k
  return pos < params.params.size();
9788
19.7k
}
9789
9790
template <>
9791
8.58k
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
9792
8.58k
  if (!has_next()) {
9793
0
    return std::nullopt;
9794
0
  }
9795
8.58k
  return params.params[pos++].first;
9796
8.58k
}
9797
9798
template <>
9799
8.58k
inline std::optional<std::string_view> url_search_params_values_iter::next() {
9800
8.58k
  if (!has_next()) {
9801
0
    return std::nullopt;
9802
0
  }
9803
8.58k
  return params.params[pos++].second;
9804
8.58k
}
9805
9806
template <>
9807
inline std::optional<key_value_view_pair>
9808
8.58k
url_search_params_entries_iter::next() {
9809
8.58k
  if (!has_next()) {
9810
0
    return std::nullopt;
9811
0
  }
9812
8.58k
  return params.params[pos++];
9813
8.58k
}
9814
9815
}  // namespace ada
9816
9817
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
9818
/* end file include/ada/url_search_params-inl.h */
9819
9820
/* begin file include/ada/url_pattern-inl.h */
9821
/**
9822
 * @file url_pattern-inl.h
9823
 * @brief Declaration for the URLPattern inline functions.
9824
 */
9825
#ifndef ADA_URL_PATTERN_INL_H
9826
#define ADA_URL_PATTERN_INL_H
9827
9828
9829
#include <algorithm>
9830
#include <string_view>
9831
#include <utility>
9832
9833
#if ADA_INCLUDE_URL_PATTERN
9834
namespace ada {
9835
9836
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
9837
0
  return protocol == other.protocol && username == other.username &&
9838
0
         password == other.password && hostname == other.hostname &&
9839
0
         port == other.port && search == other.search && hash == other.hash &&
9840
0
         pathname == other.pathname;
9841
0
}
9842
9843
inline bool url_pattern_component_result::operator==(
9844
0
    const url_pattern_component_result& other) const {
9845
0
  return input == other.input && groups == other.groups;
9846
0
}
9847
9848
template <url_pattern_regex::regex_concept regex_provider>
9849
url_pattern_component_result
9850
url_pattern_component<regex_provider>::create_component_match_result(
9851
    std::string&& input,
9852
373k
    std::vector<std::optional<std::string>>&& exec_result) {
9853
  // Let result be a new URLPatternComponentResult.
9854
  // Set result["input"] to input.
9855
  // Let groups be a record<USVString, (USVString or undefined)>.
9856
373k
  auto result =
9857
373k
      url_pattern_component_result{.input = std::move(input), .groups = {}};
9858
9859
  // We explicitly start iterating from 0 even though the spec
9860
  // says we should start from 1. This case is handled by the
9861
  // std_regex_provider which removes the full match from index 0.
9862
  // Use min() to guard against potential mismatches between
9863
  // exec_result size and group_name_list size.
9864
373k
  const size_t size = std::min(exec_result.size(), group_name_list.size());
9865
373k
  result.groups.reserve(size);
9866
739k
  for (size_t index = 0; index < size; index++) {
9867
365k
    result.groups.emplace(group_name_list[index],
9868
365k
                          std::move(exec_result[index]));
9869
365k
  }
9870
373k
  return result;
9871
373k
}
9872
9873
template <url_pattern_regex::regex_concept regex_provider>
9874
std::string_view url_pattern<regex_provider>::get_protocol() const
9875
108k
    ada_lifetime_bound {
9876
  // Return this's associated URL pattern's protocol component's pattern string.
9877
108k
  return protocol_component.pattern;
9878
108k
}
9879
template <url_pattern_regex::regex_concept regex_provider>
9880
std::string_view url_pattern<regex_provider>::get_username() const
9881
108k
    ada_lifetime_bound {
9882
  // Return this's associated URL pattern's username component's pattern string.
9883
108k
  return username_component.pattern;
9884
108k
}
9885
template <url_pattern_regex::regex_concept regex_provider>
9886
std::string_view url_pattern<regex_provider>::get_password() const
9887
108k
    ada_lifetime_bound {
9888
  // Return this's associated URL pattern's password component's pattern string.
9889
108k
  return password_component.pattern;
9890
108k
}
9891
template <url_pattern_regex::regex_concept regex_provider>
9892
std::string_view url_pattern<regex_provider>::get_hostname() const
9893
108k
    ada_lifetime_bound {
9894
  // Return this's associated URL pattern's hostname component's pattern string.
9895
108k
  return hostname_component.pattern;
9896
108k
}
9897
template <url_pattern_regex::regex_concept regex_provider>
9898
std::string_view url_pattern<regex_provider>::get_port() const
9899
108k
    ada_lifetime_bound {
9900
  // Return this's associated URL pattern's port component's pattern string.
9901
108k
  return port_component.pattern;
9902
108k
}
9903
template <url_pattern_regex::regex_concept regex_provider>
9904
std::string_view url_pattern<regex_provider>::get_pathname() const
9905
108k
    ada_lifetime_bound {
9906
  // Return this's associated URL pattern's pathname component's pattern string.
9907
108k
  return pathname_component.pattern;
9908
108k
}
9909
template <url_pattern_regex::regex_concept regex_provider>
9910
std::string_view url_pattern<regex_provider>::get_search() const
9911
108k
    ada_lifetime_bound {
9912
  // Return this's associated URL pattern's search component's pattern string.
9913
108k
  return search_component.pattern;
9914
108k
}
9915
template <url_pattern_regex::regex_concept regex_provider>
9916
std::string_view url_pattern<regex_provider>::get_hash() const
9917
108k
    ada_lifetime_bound {
9918
  // Return this's associated URL pattern's hash component's pattern string.
9919
108k
  return hash_component.pattern;
9920
108k
}
9921
template <url_pattern_regex::regex_concept regex_provider>
9922
108k
bool url_pattern<regex_provider>::ignore_case() const {
9923
108k
  return ignore_case_;
9924
108k
}
9925
template <url_pattern_regex::regex_concept regex_provider>
9926
108k
bool url_pattern<regex_provider>::has_regexp_groups() const {
9927
  // If this's associated URL pattern's has regexp groups, then return true.
9928
108k
  return protocol_component.has_regexp_groups ||
9929
107k
         username_component.has_regexp_groups ||
9930
107k
         password_component.has_regexp_groups ||
9931
107k
         hostname_component.has_regexp_groups ||
9932
107k
         port_component.has_regexp_groups ||
9933
107k
         pathname_component.has_regexp_groups ||
9934
107k
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
9935
108k
}
9936
9937
711k
inline bool url_pattern_part::is_regexp() const noexcept {
9938
711k
  return type == url_pattern_part_type::REGEXP;
9939
711k
}
9940
9941
inline std::string_view url_pattern_compile_component_options::get_delimiter()
9942
924k
    const {
9943
924k
  if (delimiter) {
9944
200k
    return {&delimiter.value(), 1};
9945
200k
  }
9946
723k
  return {};
9947
924k
}
9948
9949
inline std::string_view url_pattern_compile_component_options::get_prefix()
9950
125k
    const {
9951
125k
  if (prefix) {
9952
118k
    return {&prefix.value(), 1};
9953
118k
  }
9954
7.39k
  return {};
9955
125k
}
9956
9957
template <url_pattern_regex::regex_concept regex_provider>
9958
template <url_pattern_encoding_callback F>
9959
tl::expected<url_pattern_component<regex_provider>, errors>
9960
url_pattern_component<regex_provider>::compile(
9961
    std::string_view input, F& encoding_callback,
9962
922k
    url_pattern_compile_component_options& options) {
9963
922k
  ada_log("url_pattern_component::compile input: ", input);
9964
  // Let part list be the result of running parse a pattern string given input,
9965
  // options, and encoding callback.
9966
922k
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
9967
922k
                                                             encoding_callback);
9968
9969
922k
  if (!part_list) {
9970
5.36k
    ada_log("parse_pattern_string failed");
9971
5.36k
    return tl::unexpected(part_list.error());
9972
5.36k
  }
9973
9974
  // Detect pattern type early to potentially skip expensive regex compilation
9975
917k
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
9976
917k
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
9977
9978
917k
  url_pattern_component_type component_type =
9979
917k
      url_pattern_component_type::REGEXP;
9980
917k
  std::string exact_match_value{};
9981
9982
917k
  if (part_list->empty()) {
9983
217k
    component_type = url_pattern_component_type::EMPTY;
9984
699k
  } else if (part_list->size() == 1) {
9985
695k
    const auto& part = (*part_list)[0];
9986
695k
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
9987
212k
        part.modifier == url_pattern_part_modifier::none &&
9988
212k
        !options.ignore_case) {
9989
212k
      component_type = url_pattern_component_type::EXACT_MATCH;
9990
212k
      exact_match_value = part.value;
9991
483k
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
9992
481k
               part.modifier == url_pattern_part_modifier::none &&
9993
481k
               part.prefix.empty() && part.suffix.empty()) {
9994
443k
      component_type = url_pattern_component_type::FULL_WILDCARD;
9995
443k
    }
9996
695k
  }
9997
9998
  // For simple patterns, skip regex generation and compilation entirely
9999
917k
  if (component_type != url_pattern_component_type::REGEXP) {
10000
873k
    auto pattern_string =
10001
873k
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10002
    // For FULL_WILDCARD, we need the group name from
10003
    // generate_regular_expression
10004
873k
    std::vector<std::string> name_list;
10005
873k
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10006
443k
        !part_list->empty()) {
10007
443k
      name_list.push_back((*part_list)[0].name);
10008
443k
    }
10009
873k
    return url_pattern_component<regex_provider>(
10010
873k
        std::move(pattern_string), typename regex_provider::regex_type{},
10011
873k
        std::move(name_list), has_regexp_groups, component_type,
10012
873k
        std::move(exact_match_value));
10013
873k
  }
10014
10015
  // Generate regex for complex patterns
10016
43.9k
  auto [regular_expression_string, name_list] =
10017
43.9k
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10018
43.9k
                                                                     options);
10019
43.9k
  auto pattern_string =
10020
43.9k
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10021
10022
43.9k
  std::optional<typename regex_provider::regex_type> regular_expression =
10023
43.9k
      regex_provider::create_instance(regular_expression_string,
10024
43.9k
                                      options.ignore_case);
10025
43.9k
  if (!regular_expression) {
10026
1.19k
    return tl::unexpected(errors::type_error);
10027
1.19k
  }
10028
10029
42.7k
  return url_pattern_component<regex_provider>(
10030
42.7k
      std::move(pattern_string), std::move(*regular_expression),
10031
42.7k
      std::move(name_list), has_regexp_groups, component_type,
10032
42.7k
      std::move(exact_match_value));
10033
43.9k
}
10034
10035
template <url_pattern_regex::regex_concept regex_provider>
10036
bool url_pattern_component<regex_provider>::fast_test(
10037
750k
    std::string_view input) const noexcept {
10038
  // Fast path for simple patterns - avoid regex evaluation
10039
  // Using if-else for better branch prediction on common cases
10040
750k
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10041
440k
    return true;
10042
440k
  }
10043
310k
  if (type == url_pattern_component_type::EXACT_MATCH) {
10044
179k
    return input == exact_match_value;
10045
179k
  }
10046
131k
  if (type == url_pattern_component_type::EMPTY) {
10047
124k
    return input.empty();
10048
124k
  }
10049
  // type == REGEXP
10050
7.05k
  return regex_provider::regex_match(input, regexp);
10051
131k
}
10052
10053
template <url_pattern_regex::regex_concept regex_provider>
10054
std::optional<std::vector<std::optional<std::string>>>
10055
url_pattern_component<regex_provider>::fast_match(
10056
808k
    std::string_view input) const {
10057
  // Handle each type directly without redundant checks
10058
808k
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10059
    // FULL_WILDCARD always matches - capture the input (even if empty)
10060
    // If there's no group name, return empty groups
10061
478k
    if (group_name_list.empty()) {
10062
0
      return std::vector<std::optional<std::string>>{};
10063
0
    }
10064
    // Capture the matched input (including empty strings)
10065
478k
    return std::vector<std::optional<std::string>>{std::string(input)};
10066
478k
  }
10067
329k
  if (type == url_pattern_component_type::EXACT_MATCH) {
10068
197k
    if (input == exact_match_value) {
10069
58.9k
      return std::vector<std::optional<std::string>>{};
10070
58.9k
    }
10071
138k
    return std::nullopt;
10072
197k
  }
10073
131k
  if (type == url_pattern_component_type::EMPTY) {
10074
124k
    if (input.empty()) {
10075
106k
      return std::vector<std::optional<std::string>>{};
10076
106k
    }
10077
18.3k
    return std::nullopt;
10078
124k
  }
10079
  // type == REGEXP - use regex
10080
7.20k
  return regex_provider::regex_search(input, regexp);
10081
131k
}
10082
10083
template <url_pattern_regex::regex_concept regex_provider>
10084
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10085
324k
    const url_pattern_input& input, const std::string_view* base_url) {
10086
  // Return the result of match given this's associated URL pattern, input, and
10087
  // baseURL if given.
10088
324k
  return match(input, base_url);
10089
324k
}
10090
10091
template <url_pattern_regex::regex_concept regex_provider>
10092
bool url_pattern<regex_provider>::test_components(
10093
    std::string_view protocol, std::string_view username,
10094
    std::string_view password, std::string_view hostname, std::string_view port,
10095
    std::string_view pathname, std::string_view search,
10096
207k
    std::string_view hash) const {
10097
207k
  return protocol_component.fast_test(protocol) &&
10098
106k
         username_component.fast_test(username) &&
10099
105k
         password_component.fast_test(password) &&
10100
105k
         hostname_component.fast_test(hostname) &&
10101
66.1k
         port_component.fast_test(port) &&
10102
65.9k
         pathname_component.fast_test(pathname) &&
10103
46.7k
         search_component.fast_test(search) && hash_component.fast_test(hash);
10104
207k
}
10105
10106
template <url_pattern_regex::regex_concept regex_provider>
10107
result<bool> url_pattern<regex_provider>::test(
10108
324k
    const url_pattern_input& input, const std::string_view* base_url_string) {
10109
  // If input is a URLPatternInit
10110
324k
  if (std::holds_alternative<url_pattern_init>(input)) {
10111
108k
    if (base_url_string) {
10112
0
      return tl::unexpected(errors::type_error);
10113
0
    }
10114
10115
108k
    std::string protocol{}, username{}, password{}, hostname{};
10116
108k
    std::string port{}, pathname{}, search{}, hash{};
10117
10118
108k
    auto apply_result = url_pattern_init::process(
10119
108k
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10120
108k
        protocol, username, password, hostname, port, pathname, search, hash);
10121
10122
108k
    if (!apply_result) {
10123
181
      return false;
10124
181
    }
10125
10126
107k
    std::string_view search_view = *apply_result->search;
10127
107k
    if (search_view.starts_with("?")) {
10128
0
      search_view.remove_prefix(1);
10129
0
    }
10130
10131
107k
    return test_components(*apply_result->protocol, *apply_result->username,
10132
107k
                           *apply_result->password, *apply_result->hostname,
10133
107k
                           *apply_result->port, *apply_result->pathname,
10134
107k
                           search_view, *apply_result->hash);
10135
108k
  }
10136
10137
  // URL string input path
10138
216k
  result<url_aggregator> base_url;
10139
216k
  if (base_url_string) {
10140
108k
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10141
108k
    if (!base_url) {
10142
0
      return false;
10143
0
    }
10144
108k
  }
10145
10146
216k
  auto url =
10147
216k
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10148
216k
                                 base_url.has_value() ? &*base_url : nullptr);
10149
216k
  if (!url) {
10150
149k
    return false;
10151
149k
  }
10152
10153
  // Extract components as string_view
10154
66.3k
  auto protocol_view = url->get_protocol();
10155
66.3k
  if (protocol_view.ends_with(":")) {
10156
66.3k
    protocol_view.remove_suffix(1);
10157
66.3k
  }
10158
10159
66.3k
  auto search_view = url->get_search();
10160
66.3k
  if (search_view.starts_with("?")) {
10161
3.44k
    search_view.remove_prefix(1);
10162
3.44k
  }
10163
10164
66.3k
  auto hash_view = url->get_hash();
10165
66.3k
  if (hash_view.starts_with("#")) {
10166
2.63k
    hash_view.remove_prefix(1);
10167
2.63k
  }
10168
10169
66.3k
  return test_components(protocol_view, url->get_username(),
10170
66.3k
                         url->get_password(), url->get_hostname(),
10171
66.3k
                         url->get_port(), url->get_pathname(), search_view,
10172
66.3k
                         hash_view);
10173
216k
}
10174
10175
template <url_pattern_regex::regex_concept regex_provider>
10176
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10177
432k
    const url_pattern_input& input, const std::string_view* base_url_string) {
10178
432k
  std::string protocol{};
10179
432k
  std::string username{};
10180
432k
  std::string password{};
10181
432k
  std::string hostname{};
10182
432k
  std::string port{};
10183
432k
  std::string pathname{};
10184
432k
  std::string search{};
10185
432k
  std::string hash{};
10186
10187
  // Let inputs be an empty list.
10188
  // Append input to inputs.
10189
432k
  std::vector inputs{input};
10190
10191
  // If input is a URLPatternInit then:
10192
432k
  if (std::holds_alternative<url_pattern_init>(input)) {
10193
108k
    ada_log(
10194
108k
        "url_pattern::match called with url_pattern_init and base_url_string=",
10195
108k
        base_url_string);
10196
    // If baseURLString was given, throw a TypeError.
10197
108k
    if (base_url_string) {
10198
0
      ada_log("failed to match because base_url_string was given");
10199
0
      return tl::unexpected(errors::type_error);
10200
0
    }
10201
10202
    // Let applyResult be the result of process a URLPatternInit given input,
10203
    // "url", protocol, username, password, hostname, port, pathname, search,
10204
    // and hash.
10205
108k
    auto apply_result = url_pattern_init::process(
10206
108k
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10207
108k
        protocol, username, password, hostname, port, pathname, search, hash);
10208
10209
    // If this throws an exception, catch it, and return null.
10210
108k
    if (!apply_result.has_value()) {
10211
181
      ada_log("match returned std::nullopt because process threw");
10212
181
      return std::nullopt;
10213
181
    }
10214
10215
    // Set protocol to applyResult["protocol"].
10216
107k
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10217
107k
    protocol = std::move(apply_result->protocol.value());
10218
10219
    // Set username to applyResult["username"].
10220
107k
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10221
107k
    username = std::move(apply_result->username.value());
10222
10223
    // Set password to applyResult["password"].
10224
107k
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10225
107k
    password = std::move(apply_result->password.value());
10226
10227
    // Set hostname to applyResult["hostname"].
10228
107k
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10229
107k
    hostname = std::move(apply_result->hostname.value());
10230
10231
    // Set port to applyResult["port"].
10232
107k
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10233
107k
    port = std::move(apply_result->port.value());
10234
10235
    // Set pathname to applyResult["pathname"].
10236
107k
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10237
107k
    pathname = std::move(apply_result->pathname.value());
10238
10239
    // Set search to applyResult["search"].
10240
107k
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10241
107k
    if (apply_result->search->starts_with("?")) {
10242
0
      search = apply_result->search->substr(1);
10243
107k
    } else {
10244
107k
      search = std::move(apply_result->search.value());
10245
107k
    }
10246
10247
    // Set hash to applyResult["hash"].
10248
107k
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10249
107k
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10250
107k
    hash = std::move(apply_result->hash.value());
10251
324k
  } else {
10252
324k
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10253
10254
    // Let baseURL be null.
10255
324k
    result<url_aggregator> base_url;
10256
10257
    // If baseURLString was given, then:
10258
324k
    if (base_url_string) {
10259
      // Let baseURL be the result of parsing baseURLString.
10260
108k
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10261
10262
      // If baseURL is failure, return null.
10263
108k
      if (!base_url) {
10264
0
        ada_log("match returned std::nullopt because failed to parse base_url=",
10265
0
                *base_url_string);
10266
0
        return std::nullopt;
10267
0
      }
10268
10269
      // Append baseURLString to inputs.
10270
108k
      inputs.emplace_back(*base_url_string);
10271
108k
    }
10272
10273
324k
    url_aggregator* base_url_value =
10274
324k
        base_url.has_value() ? &*base_url : nullptr;
10275
10276
    // Set url to the result of parsing input given baseURL.
10277
324k
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10278
324k
                                          base_url_value);
10279
10280
    // If url is failure, return null.
10281
324k
    if (!url) {
10282
224k
      ada_log("match returned std::nullopt because url failed");
10283
224k
      return std::nullopt;
10284
224k
    }
10285
10286
    // Set protocol to url's scheme.
10287
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10288
    // is removed. Similar work was done on workerd:
10289
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10290
99.5k
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10291
    // Set username to url's username.
10292
99.5k
    username = url->get_username();
10293
    // Set password to url's password.
10294
99.5k
    password = url->get_password();
10295
    // Set hostname to url's host, serialized, or the empty string if the value
10296
    // is null.
10297
99.5k
    hostname = url->get_hostname();
10298
    // Set port to url's port, serialized, or the empty string if the value is
10299
    // null.
10300
99.5k
    port = url->get_port();
10301
    // Set pathname to the result of URL path serializing url.
10302
99.5k
    pathname = url->get_pathname();
10303
    // Set search to url's query or the empty string if the value is null.
10304
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10305
    // is removed. Similar work was done on workerd:
10306
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10307
99.5k
    if (url->has_search()) {
10308
5.48k
      auto view = url->get_search();
10309
5.48k
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10310
5.48k
    }
10311
    // Set hash to url's fragment or the empty string if the value is null.
10312
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10313
    // removed. Similar work was done on workerd:
10314
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10315
99.5k
    if (url->has_hash()) {
10316
4.29k
      auto view = url->get_hash();
10317
4.29k
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10318
4.29k
    }
10319
99.5k
  }
10320
10321
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10322
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10323
10324
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10325
  // component's regular expression, protocol).
10326
207k
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10327
207k
  if (!protocol_exec_result) {
10328
81.7k
    return std::nullopt;
10329
81.7k
  }
10330
10331
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10332
  // component's regular expression, username).
10333
125k
  auto username_exec_result = username_component.fast_match(username);
10334
125k
  if (!username_exec_result) {
10335
1.14k
    return std::nullopt;
10336
1.14k
  }
10337
10338
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10339
  // component's regular expression, password).
10340
124k
  auto password_exec_result = password_component.fast_match(password);
10341
124k
  if (!password_exec_result) {
10342
399
    return std::nullopt;
10343
399
  }
10344
10345
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10346
  // component's regular expression, hostname).
10347
124k
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10348
124k
  if (!hostname_exec_result) {
10349
57.8k
    return std::nullopt;
10350
57.8k
  }
10351
10352
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10353
  // regular expression, port).
10354
66.3k
  auto port_exec_result = port_component.fast_match(port);
10355
66.3k
  if (!port_exec_result) {
10356
257
    return std::nullopt;
10357
257
  }
10358
10359
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10360
  // component's regular expression, pathname).
10361
66.1k
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10362
66.1k
  if (!pathname_exec_result) {
10363
19.1k
    return std::nullopt;
10364
19.1k
  }
10365
10366
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10367
  // regular expression, search).
10368
47.0k
  auto search_exec_result = search_component.fast_match(search);
10369
47.0k
  if (!search_exec_result) {
10370
241
    return std::nullopt;
10371
241
  }
10372
10373
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10374
  // regular expression, hash).
10375
46.7k
  auto hash_exec_result = hash_component.fast_match(hash);
10376
46.7k
  if (!hash_exec_result) {
10377
100
    return std::nullopt;
10378
100
  }
10379
10380
  // Let result be a new URLPatternResult.
10381
46.6k
  auto result = url_pattern_result{};
10382
  // Set result["inputs"] to inputs.
10383
46.6k
  result.inputs = std::move(inputs);
10384
  // Set result["protocol"] to the result of creating a component match result
10385
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10386
46.6k
  result.protocol = protocol_component.create_component_match_result(
10387
46.6k
      std::move(protocol), std::move(*protocol_exec_result));
10388
10389
  // Set result["username"] to the result of creating a component match result
10390
  // given urlPattern's username component, username, and usernameExecResult.
10391
46.6k
  result.username = username_component.create_component_match_result(
10392
46.6k
      std::move(username), std::move(*username_exec_result));
10393
10394
  // Set result["password"] to the result of creating a component match result
10395
  // given urlPattern's password component, password, and passwordExecResult.
10396
46.6k
  result.password = password_component.create_component_match_result(
10397
46.6k
      std::move(password), std::move(*password_exec_result));
10398
10399
  // Set result["hostname"] to the result of creating a component match result
10400
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10401
46.6k
  result.hostname = hostname_component.create_component_match_result(
10402
46.6k
      std::move(hostname), std::move(*hostname_exec_result));
10403
10404
  // Set result["port"] to the result of creating a component match result given
10405
  // urlPattern's port component, port, and portExecResult.
10406
46.6k
  result.port = port_component.create_component_match_result(
10407
46.6k
      std::move(port), std::move(*port_exec_result));
10408
10409
  // Set result["pathname"] to the result of creating a component match result
10410
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10411
46.6k
  result.pathname = pathname_component.create_component_match_result(
10412
46.6k
      std::move(pathname), std::move(*pathname_exec_result));
10413
10414
  // Set result["search"] to the result of creating a component match result
10415
  // given urlPattern's search component, search, and searchExecResult.
10416
46.6k
  result.search = search_component.create_component_match_result(
10417
46.6k
      std::move(search), std::move(*search_exec_result));
10418
10419
  // Set result["hash"] to the result of creating a component match result given
10420
  // urlPattern's hash component, hash, and hashExecResult.
10421
46.6k
  result.hash = hash_component.create_component_match_result(
10422
46.6k
      std::move(hash), std::move(*hash_exec_result));
10423
10424
46.6k
  return result;
10425
46.7k
}
10426
10427
}  // namespace ada
10428
#endif  // ADA_INCLUDE_URL_PATTERN
10429
#endif
10430
/* end file include/ada/url_pattern-inl.h */
10431
/* begin file include/ada/url_pattern_helpers-inl.h */
10432
/**
10433
 * @file url_pattern_helpers-inl.h
10434
 * @brief Declaration for the URLPattern helpers.
10435
 */
10436
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10437
#define ADA_URL_PATTERN_HELPERS_INL_H
10438
10439
#include <optional>
10440
#include <string_view>
10441
10442
10443
#if ADA_INCLUDE_URL_PATTERN
10444
namespace ada::url_pattern_helpers {
10445
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10446
0
inline std::string to_string(token_type type) {
10447
0
  switch (type) {
10448
0
    case token_type::INVALID_CHAR:
10449
0
      return "INVALID_CHAR";
10450
0
    case token_type::OPEN:
10451
0
      return "OPEN";
10452
0
    case token_type::CLOSE:
10453
0
      return "CLOSE";
10454
0
    case token_type::REGEXP:
10455
0
      return "REGEXP";
10456
0
    case token_type::NAME:
10457
0
      return "NAME";
10458
0
    case token_type::CHAR:
10459
0
      return "CHAR";
10460
0
    case token_type::ESCAPED_CHAR:
10461
0
      return "ESCAPED_CHAR";
10462
0
    case token_type::OTHER_MODIFIER:
10463
0
      return "OTHER_MODIFIER";
10464
0
    case token_type::ASTERISK:
10465
0
      return "ASTERISK";
10466
0
    case token_type::END:
10467
0
      return "END";
10468
0
    default:
10469
0
      ada::unreachable();
10470
0
  }
10471
0
}
10472
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10473
10474
template <url_pattern_regex::regex_concept regex_provider>
10475
112k
constexpr void constructor_string_parser<regex_provider>::rewind() {
10476
  // Set parser's token index to parser's component start.
10477
112k
  token_index = component_start;
10478
  // Set parser's token increment to 0.
10479
112k
  token_increment = 0;
10480
112k
}
10481
10482
template <url_pattern_regex::regex_concept regex_provider>
10483
1.01M
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10484
  // Return the result of running is a non-special pattern char given parser,
10485
  // parser's token index and "#".
10486
1.01M
  return is_non_special_pattern_char(token_index, '#');
10487
1.01M
}
10488
10489
template <url_pattern_regex::regex_concept regex_provider>
10490
1.01M
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10491
  // If result of running is a non-special pattern char given parser, parser's
10492
  // token index and "?" is true, then return true.
10493
1.01M
  if (is_non_special_pattern_char(token_index, '?')) {
10494
3
    return true;
10495
3
  }
10496
10497
  // If parser's token list[parser's token index]'s value is not "?", then
10498
  // return false.
10499
1.01M
  if (token_list[token_index].value != "?") {
10500
1.01M
    return false;
10501
1.01M
  }
10502
10503
  // If previous index is less than 0, then return true.
10504
1.06k
  if (token_index == 0) return true;
10505
  // Let previous index be parser's token index - 1.
10506
1.06k
  auto previous_index = token_index - 1;
10507
  // Let previous token be the result of running get a safe token given parser
10508
  // and previous index.
10509
1.06k
  auto previous_token = get_safe_token(previous_index);
10510
1.06k
  ADA_ASSERT_TRUE(previous_token);
10511
  // If any of the following are true, then return false:
10512
  // - previous token's type is "name".
10513
  // - previous token's type is "regexp".
10514
  // - previous token's type is "close".
10515
  // - previous token's type is "asterisk".
10516
1.06k
  return !(previous_token->type == token_type::NAME ||
10517
987
           previous_token->type == token_type::REGEXP ||
10518
906
           previous_token->type == token_type::CLOSE ||
10519
552
           previous_token->type == token_type::ASTERISK);
10520
1.06k
}
10521
10522
template <url_pattern_regex::regex_concept regex_provider>
10523
constexpr bool
10524
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10525
5.37M
    size_t index, uint32_t value) const {
10526
  // Let token be the result of running get a safe token given parser and index.
10527
5.37M
  auto token = get_safe_token(index);
10528
5.37M
  ADA_ASSERT_TRUE(token);
10529
10530
  // If token's value is not value, then return false.
10531
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10532
  // string.
10533
5.37M
  if (!token->value.empty() &&
10534
5.37M
      static_cast<uint32_t>(token->value[0]) != value) {
10535
5.14M
    return false;
10536
5.14M
  }
10537
10538
  // If any of the following are true:
10539
  // - token's type is "char";
10540
  // - token's type is "escaped-char"; or
10541
  // - token's type is "invalid-char",
10542
  // - then return true.
10543
229k
  return token->type == token_type::CHAR ||
10544
79.7k
         token->type == token_type::ESCAPED_CHAR ||
10545
79.6k
         token->type == token_type::INVALID_CHAR;
10546
5.37M
}
10547
10548
template <url_pattern_regex::regex_concept regex_provider>
10549
constexpr const token*
10550
5.56M
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10551
  // If index is less than parser's token list's size, then return parser's
10552
  // token list[index].
10553
5.56M
  if (index < token_list.size()) [[likely]] {
10554
5.56M
    return &token_list[index];
10555
5.56M
  }
10556
10557
  // Assert: parser's token list's size is greater than or equal to 1.
10558
0
  ADA_ASSERT_TRUE(!token_list.empty());
10559
10560
  // Let token be parser's token list[last index].
10561
  // Assert: token's type is "end".
10562
0
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10563
10564
  // Return token.
10565
0
  return &token_list.back();
10566
5.56M
}
10567
10568
template <url_pattern_regex::regex_concept regex_provider>
10569
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10570
1.64M
    const {
10571
  // If parser's token list[parser's token index]'s type is "open", then return
10572
  // true.
10573
1.64M
  return token_list[token_index].type == token_type::OPEN;
10574
1.64M
}
10575
10576
template <url_pattern_regex::regex_concept regex_provider>
10577
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10578
12.9k
    const {
10579
  // If parser's token list[parser's token index]'s type is "close", then return
10580
  // true.
10581
12.9k
  return token_list[token_index].type == token_type::CLOSE;
10582
12.9k
}
10583
10584
template <url_pattern_regex::regex_concept regex_provider>
10585
constexpr bool
10586
37.4k
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10587
  // If the result of running is a non-special pattern char given parser,
10588
  // parser's token index + 1, and "/" is false, then return false.
10589
37.4k
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10590
0
    return false;
10591
0
  }
10592
  // If the result of running is a non-special pattern char given parser,
10593
  // parser's token index + 2, and "/" is false, then return false.
10594
37.4k
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10595
0
    return false;
10596
0
  }
10597
37.4k
  return true;
10598
37.4k
}
10599
10600
template <url_pattern_regex::regex_concept regex_provider>
10601
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10602
576k
    const {
10603
  // Return the result of running is a non-special pattern char given parser,
10604
  // parser's token index, and ":".
10605
576k
  return is_non_special_pattern_char(token_index, ':');
10606
576k
}
10607
10608
template <url_pattern_regex::regex_concept regex_provider>
10609
void constructor_string_parser<regex_provider>::change_state(State new_state,
10610
260k
                                                             size_t skip) {
10611
  // If parser's state is not "init", not "authority", and not "done", then set
10612
  // parser's result[parser's state] to the result of running make a component
10613
  // string given parser.
10614
260k
  if (state != State::INIT && state != State::AUTHORITY &&
10615
148k
      state != State::DONE) {
10616
148k
    auto value = make_component_string();
10617
    // TODO: Simplify this.
10618
148k
    switch (state) {
10619
37.4k
      case State::PROTOCOL: {
10620
37.4k
        result.protocol = value;
10621
37.4k
        break;
10622
0
      }
10623
0
      case State::USERNAME: {
10624
0
        result.username = value;
10625
0
        break;
10626
0
      }
10627
0
      case State::PASSWORD: {
10628
0
        result.password = value;
10629
0
        break;
10630
0
      }
10631
37.4k
      case State::HOSTNAME: {
10632
37.4k
        result.hostname = value;
10633
37.4k
        break;
10634
0
      }
10635
0
      case State::PORT: {
10636
0
        result.port = value;
10637
0
        break;
10638
0
      }
10639
73.3k
      case State::PATHNAME: {
10640
73.3k
        result.pathname = value;
10641
73.3k
        break;
10642
0
      }
10643
261
      case State::SEARCH: {
10644
261
        result.search = value;
10645
261
        break;
10646
0
      }
10647
204
      case State::HASH: {
10648
204
        result.hash = value;
10649
204
        break;
10650
0
      }
10651
0
      default:
10652
0
        ada::unreachable();
10653
148k
    }
10654
148k
  }
10655
10656
  // If parser's state is not "init" and new state is not "done", then:
10657
260k
  if (state != State::INIT && new_state != State::DONE) {
10658
    // If parser's state is "protocol", "authority", "username", or "password";
10659
    // new state is "port", "pathname", "search", or "hash"; and parser's
10660
    // result["hostname"] does not exist, then set parser's result["hostname"]
10661
    // to the empty string.
10662
112k
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10663
37.8k
         state == State::USERNAME || state == State::PASSWORD) &&
10664
74.8k
        (new_state == State::PORT || new_state == State::PATHNAME ||
10665
74.8k
         new_state == State::SEARCH || new_state == State::HASH) &&
10666
0
        !result.hostname)
10667
0
      result.hostname = "";
10668
112k
  }
10669
10670
  // If parser's state is "protocol", "authority", "username", "password",
10671
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10672
  // result["pathname"] does not exist, then:
10673
260k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10674
185k
       state == State::USERNAME || state == State::PASSWORD ||
10675
185k
       state == State::HOSTNAME || state == State::PORT) &&
10676
112k
      (new_state == State::SEARCH || new_state == State::HASH) &&
10677
0
      !result.pathname) {
10678
0
    if (protocol_matches_a_special_scheme_flag) {
10679
0
      result.pathname = "/";
10680
0
    } else {
10681
      // Otherwise, set parser's result["pathname"] to the empty string.
10682
0
      result.pathname = "";
10683
0
    }
10684
0
  }
10685
10686
  // If parser's state is "protocol", "authority", "username", "password",
10687
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10688
  // result["search"] does not exist, then set parser's result["search"] to
10689
  // the empty string.
10690
260k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10691
185k
       state == State::USERNAME || state == State::PASSWORD ||
10692
185k
       state == State::HOSTNAME || state == State::PORT ||
10693
148k
       state == State::PATHNAME) &&
10694
185k
      new_state == State::HASH && !result.search) {
10695
168
    result.search = "";
10696
168
  }
10697
10698
  // Set parser's state to new state.
10699
260k
  state = new_state;
10700
  // Increment parser's token index by skip.
10701
260k
  token_index += skip;
10702
  // Set parser's component start to parser's token index.
10703
260k
  component_start = token_index;
10704
  // Set parser's token increment to 0.
10705
260k
  token_increment = 0;
10706
260k
}
10707
10708
template <url_pattern_regex::regex_concept regex_provider>
10709
187k
std::string constructor_string_parser<regex_provider>::make_component_string() {
10710
  // Assert: parser's token index is less than parser's token list's size.
10711
187k
  ADA_ASSERT_TRUE(token_index < token_list.size());
10712
10713
  // Let token be parser's token list[parser's token index].
10714
  // Let end index be token's index.
10715
187k
  const auto end_index = token_list[token_index].index;
10716
  // Let component start token be the result of running get a safe token given
10717
  // parser and parser's component start.
10718
187k
  const auto component_start_token = get_safe_token(component_start);
10719
187k
  ADA_ASSERT_TRUE(component_start_token);
10720
  // Let component start input index be component start token's index.
10721
187k
  const auto component_start_input_index = component_start_token->index;
10722
  // Return the code point substring from component start input index to end
10723
  // index within parser's input.
10724
187k
  return std::string(input.substr(component_start_input_index,
10725
187k
                                  end_index - component_start_input_index));
10726
187k
}
10727
10728
template <url_pattern_regex::regex_concept regex_provider>
10729
constexpr bool
10730
448k
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
10731
  // Return the result of running is a non-special pattern char given parser,
10732
  // parser's token index, and "@".
10733
448k
  return is_non_special_pattern_char(token_index, '@');
10734
448k
}
10735
10736
template <url_pattern_regex::regex_concept regex_provider>
10737
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
10738
897k
    const {
10739
  // Return the result of running is a non-special pattern char given parser,
10740
  // parser's token index, and "/".
10741
897k
  return is_non_special_pattern_char(token_index, '/');
10742
897k
}
10743
10744
template <url_pattern_regex::regex_concept regex_provider>
10745
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
10746
0
    const {
10747
  // Return the result of running is a non-special pattern char given parser,
10748
  // parser's token index, and ":".
10749
0
  return is_non_special_pattern_char(token_index, ':');
10750
0
}
10751
10752
template <url_pattern_regex::regex_concept regex_provider>
10753
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
10754
448k
    const {
10755
  // Return the result of running is a non-special pattern char given parser,
10756
  // parser's token index, and "[".
10757
448k
  return is_non_special_pattern_char(token_index, '[');
10758
448k
}
10759
10760
template <url_pattern_regex::regex_concept regex_provider>
10761
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
10762
448k
    const {
10763
  // Return the result of running is a non-special pattern char given parser,
10764
  // parser's token index, and "]".
10765
448k
  return is_non_special_pattern_char(token_index, ']');
10766
448k
}
10767
10768
template <url_pattern_regex::regex_concept regex_provider>
10769
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
10770
448k
    const {
10771
  // Return the result of running is a non-special pattern char given parser,
10772
  // parser's token index, and ":".
10773
448k
  return is_non_special_pattern_char(token_index, ':');
10774
448k
}
10775
10776
2.83M
constexpr void Tokenizer::get_next_code_point() {
10777
2.83M
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
10778
2.83M
  ADA_ASSERT_TRUE(next_index < input.size());
10779
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
10780
  // invalid, return the offending byte as the code point, and advance by one
10781
  // to guarantee forward progress.
10782
2.83M
  invalid_code_point = false;
10783
2.83M
  code_point = 0;
10784
2.83M
  size_t number_bytes = 0;
10785
2.83M
  const size_t initial_index = next_index;
10786
2.83M
  unsigned char first_byte = input[next_index];
10787
10788
2.83M
  if ((first_byte & 0x80) == 0) {
10789
    // 1-byte character (ASCII)
10790
2.83M
    next_index++;
10791
2.83M
    code_point = first_byte;
10792
2.83M
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
10793
2.83M
            uint32_t(code_point));
10794
2.83M
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10795
2.83M
            " input.size()=", input.size());
10796
2.83M
    return;
10797
2.83M
  }
10798
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
10799
0
          uint32_t(first_byte));
10800
0
  if ((first_byte & 0xE0) == 0xC0) {
10801
0
    code_point = first_byte & 0x1F;
10802
0
    number_bytes = 2;
10803
0
    ada_log("Tokenizer::get_next_code_point two bytes");
10804
0
  } else if ((first_byte & 0xF0) == 0xE0) {
10805
0
    code_point = first_byte & 0x0F;
10806
0
    number_bytes = 3;
10807
0
    ada_log("Tokenizer::get_next_code_point three bytes");
10808
0
  } else if ((first_byte & 0xF8) == 0xF0) {
10809
0
    code_point = first_byte & 0x07;
10810
0
    number_bytes = 4;
10811
0
    ada_log("Tokenizer::get_next_code_point four bytes");
10812
0
  }
10813
10814
  // Invalid leading bytes that still match a multi-byte prefix.
10815
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
10816
0
      (number_bytes == 4 && first_byte > 0xF4)) {
10817
0
    invalid_code_point = true;
10818
0
    code_point = first_byte;
10819
0
    next_index = initial_index + 1;
10820
0
    return;
10821
0
  }
10822
10823
  // Invalid leading byte (e.g., continuation byte outside a sequence).
10824
0
  if (number_bytes == 0) {
10825
0
    invalid_code_point = true;
10826
0
    code_point = first_byte;
10827
0
    next_index = initial_index + 1;
10828
0
    return;
10829
0
  }
10830
10831
  // Truncated UTF-8 sequence.
10832
0
  if (number_bytes + next_index > input.size()) {
10833
0
    invalid_code_point = true;
10834
0
    code_point = first_byte;
10835
0
    next_index = initial_index + 1;
10836
0
    return;
10837
0
  }
10838
10839
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
10840
0
    unsigned char byte = input[i];
10841
0
    if ((byte & 0xC0) != 0x80) {
10842
0
      invalid_code_point = true;
10843
0
      code_point = first_byte;
10844
0
      next_index = initial_index + 1;
10845
0
      return;
10846
0
    }
10847
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
10848
0
    code_point = (code_point << 6) | (byte & 0x3F);
10849
0
  }
10850
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
10851
0
          uint32_t(code_point));
10852
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
10853
0
          " input.size()=", input.size());
10854
0
  next_index += number_bytes;
10855
0
}
10856
10857
2.83M
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
10858
2.83M
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
10859
2.83M
          new_index);
10860
  // Set tokenizer's next index to index.
10861
2.83M
  next_index = new_index;
10862
  // Run get the next code point given tokenizer.
10863
2.83M
  get_next_code_point();
10864
2.83M
}
10865
10866
inline void Tokenizer::add_token(token_type type, size_t next_position,
10867
3.75M
                                 size_t value_position, size_t value_length) {
10868
3.75M
  ada_log("Tokenizer::add_token called with type=", to_string(type),
10869
3.75M
          " next_position=", next_position, " value_position=", value_position);
10870
3.75M
  ADA_ASSERT_TRUE(next_position >= value_position);
10871
10872
  // Let token be a new token.
10873
  // Set token's type to type.
10874
  // Set token's index to tokenizer's index.
10875
  // Set token's value to the code point substring from value position with
10876
  // length value length within tokenizer's input.
10877
  // Append token to the back of tokenizer's token list.
10878
3.75M
  token_list.emplace_back(type, index,
10879
3.75M
                          input.substr(value_position, value_length));
10880
  // Set tokenizer's index to next position.
10881
3.75M
  index = next_position;
10882
3.75M
}
10883
10884
inline void Tokenizer::add_token_with_default_length(token_type type,
10885
                                                     size_t next_position,
10886
3.74M
                                                     size_t value_position) {
10887
  // Let computed length be next position - value position.
10888
3.74M
  auto computed_length = next_position - value_position;
10889
  // Run add a token given tokenizer, type, next position, value position, and
10890
  // computed length.
10891
3.74M
  add_token(type, next_position, value_position, computed_length);
10892
3.74M
}
10893
10894
2.69M
inline void Tokenizer::add_token_with_defaults(token_type type) {
10895
2.69M
  ada_log("Tokenizer::add_token_with_defaults called with type=",
10896
2.69M
          to_string(type));
10897
  // Run add a token with default length given tokenizer, type, tokenizer's next
10898
  // index, and tokenizer's index.
10899
2.69M
  add_token_with_default_length(type, next_index, index);
10900
2.69M
}
10901
10902
inline ada_warn_unused std::optional<errors>
10903
Tokenizer::process_tokenizing_error(size_t next_position,
10904
43.9k
                                    size_t value_position) {
10905
  // If tokenizer's policy is "strict", then throw a TypeError.
10906
43.9k
  if (policy == token_policy::strict) {
10907
1.19k
    ada_log("process_tokenizing_error failed with next_position=",
10908
1.19k
            next_position, " value_position=", value_position);
10909
1.19k
    return errors::type_error;
10910
1.19k
  }
10911
  // Assert: tokenizer's policy is "lenient".
10912
42.7k
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
10913
  // Run add a token with default length given tokenizer, "invalid-char", next
10914
  // position, and value position.
10915
42.7k
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
10916
42.7k
                                value_position);
10917
42.7k
  return std::nullopt;
10918
43.9k
}
10919
10920
template <url_pattern_encoding_callback F>
10921
495k
token* url_pattern_parser<F>::try_consume_modifier_token() {
10922
  // Let token be the result of running try to consume a token given parser and
10923
  // "other-modifier".
10924
495k
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
10925
  // If token is not null, then return token.
10926
495k
  if (token) return token;
10927
  // Set token to the result of running try to consume a token given parser and
10928
  // "asterisk".
10929
  // Return token.
10930
494k
  return try_consume_token(token_type::ASTERISK);
10931
495k
}
10932
10933
template <url_pattern_encoding_callback F>
10934
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
10935
2.70M
    const token* name_token) {
10936
  // Let token be the result of running try to consume a token given parser and
10937
  // "regexp".
10938
2.70M
  auto token = try_consume_token(token_type::REGEXP);
10939
  // If name token is null and token is null, then set token to the result of
10940
  // running try to consume a token given parser and "asterisk".
10941
2.70M
  if (!name_token && !token) {
10942
2.70M
    token = try_consume_token(token_type::ASTERISK);
10943
2.70M
  }
10944
  // Return token.
10945
2.70M
  return token;
10946
2.70M
}
10947
10948
template <url_pattern_encoding_callback F>
10949
14.6M
token* url_pattern_parser<F>::try_consume_token(token_type type) {
10950
14.6M
  ada_log("url_pattern_parser::try_consume_token called with type=",
10951
14.6M
          to_string(type));
10952
  // Assert: parser's index is less than parser's token list size.
10953
14.6M
  ADA_ASSERT_TRUE(index < tokens.size());
10954
  // Let next token be parser's token list[parser's index].
10955
14.6M
  auto& next_token = tokens[index];
10956
  // If next token's type is not type return null.
10957
14.6M
  if (next_token.type != type) return nullptr;
10958
  // Increase parser's index by 1.
10959
2.76M
  index++;
10960
  // Return next token.
10961
2.76M
  return &next_token;
10962
14.6M
}
10963
10964
template <url_pattern_encoding_callback F>
10965
5.42k
std::string url_pattern_parser<F>::consume_text() {
10966
  // Let result be the empty string.
10967
5.42k
  std::string result{};
10968
  // While true:
10969
14.8k
  while (true) {
10970
    // Let token be the result of running try to consume a token given parser
10971
    // and "char".
10972
14.8k
    auto token = try_consume_token(token_type::CHAR);
10973
    // If token is null, then set token to the result of running try to consume
10974
    // a token given parser and "escaped-char".
10975
14.8k
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
10976
    // If token is null, then break.
10977
14.8k
    if (!token) break;
10978
    // Append token's value to the end of result.
10979
9.44k
    result.append(token->value);
10980
9.44k
  }
10981
  // Return result.
10982
5.42k
  return result;
10983
5.42k
}
10984
10985
template <url_pattern_encoding_callback F>
10986
920k
bool url_pattern_parser<F>::consume_required_token(token_type type) {
10987
920k
  ada_log("url_pattern_parser::consume_required_token called with type=",
10988
920k
          to_string(type));
10989
  // Let result be the result of running try to consume a token given parser and
10990
  // type.
10991
920k
  return try_consume_token(type) != nullptr;
10992
920k
}
10993
10994
template <url_pattern_encoding_callback F>
10995
std::optional<errors>
10996
1.90M
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
10997
  // If parser's pending fixed value is the empty string, then return.
10998
1.90M
  if (pending_fixed_value.empty()) {
10999
1.68M
    ada_log("pending_fixed_value is empty");
11000
1.68M
    return std::nullopt;
11001
1.68M
  }
11002
  // Let encoded value be the result of running parser's encoding callback given
11003
  // parser's pending fixed value.
11004
222k
  auto encoded_value = encoding_callback(pending_fixed_value);
11005
222k
  if (!encoded_value) {
11006
3.40k
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11007
3.40k
    return encoded_value.error();
11008
3.40k
  }
11009
  // Set parser's pending fixed value to the empty string.
11010
219k
  pending_fixed_value.clear();
11011
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11012
  // and modifier is "none".
11013
  // Append part to parser's part list.
11014
219k
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11015
219k
                     std::move(*encoded_value),
11016
219k
                     url_pattern_part_modifier::none);
11017
219k
  return std::nullopt;
11018
222k
}
11019
11020
template <url_pattern_encoding_callback F>
11021
std::optional<errors> url_pattern_parser<F>::add_part(
11022
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11023
495k
    std::string_view suffix, token* modifier_token) {
11024
  // Let modifier be "none".
11025
495k
  auto modifier = url_pattern_part_modifier::none;
11026
  // If modifier token is not null:
11027
495k
  if (modifier_token) {
11028
    // If modifier token's value is "?" then set modifier to "optional".
11029
4.19k
    if (modifier_token->value == "?") {
11030
446
      modifier = url_pattern_part_modifier::optional;
11031
3.74k
    } else if (modifier_token->value == "*") {
11032
      // Otherwise if modifier token's value is "*" then set modifier to
11033
      // "zero-or-more".
11034
3.04k
      modifier = url_pattern_part_modifier::zero_or_more;
11035
3.04k
    } else if (modifier_token->value == "+") {
11036
      // Otherwise if modifier token's value is "+" then set modifier to
11037
      // "one-or-more".
11038
702
      modifier = url_pattern_part_modifier::one_or_more;
11039
702
    }
11040
4.19k
  }
11041
  // If name token is null and regexp or wildcard token is null and modifier
11042
  // is "none":
11043
495k
  if (!name_token && !regexp_or_wildcard_token &&
11044
1.30k
      modifier == url_pattern_part_modifier::none) {
11045
    // Append prefix to the end of parser's pending fixed value.
11046
845
    pending_fixed_value.append(prefix);
11047
845
    return std::nullopt;
11048
845
  }
11049
  // Run maybe add a part from the pending fixed value given parser.
11050
494k
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11051
46
    return *error;
11052
46
  }
11053
  // If name token is null and regexp or wildcard token is null:
11054
494k
  if (!name_token && !regexp_or_wildcard_token) {
11055
    // Assert: suffix is the empty string.
11056
442
    ADA_ASSERT_TRUE(suffix.empty());
11057
    // If prefix is the empty string, then return.
11058
442
    if (prefix.empty()) return std::nullopt;
11059
    // Let encoded value be the result of running parser's encoding callback
11060
    // given prefix.
11061
358
    auto encoded_value = encoding_callback(prefix);
11062
358
    if (!encoded_value) {
11063
10
      return encoded_value.error();
11064
10
    }
11065
    // Let part be a new part whose type is "fixed-text", value is encoded
11066
    // value, and modifier is modifier.
11067
    // Append part to parser's part list.
11068
348
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11069
348
                       std::move(*encoded_value), modifier);
11070
348
    return std::nullopt;
11071
358
  }
11072
  // Let regexp value be the empty string.
11073
493k
  std::string regexp_value{};
11074
  // If regexp or wildcard token is null, then set regexp value to parser's
11075
  // segment wildcard regexp.
11076
493k
  if (!regexp_or_wildcard_token) {
11077
1.74k
    regexp_value = segment_wildcard_regexp;
11078
492k
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11079
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11080
    // regexp value to the full wildcard regexp value.
11081
489k
    regexp_value = ".*";
11082
489k
  } else {
11083
    // Otherwise set regexp value to regexp or wildcard token's value.
11084
2.44k
    regexp_value = regexp_or_wildcard_token->value;
11085
2.44k
  }
11086
  // Let type be "regexp".
11087
493k
  auto type = url_pattern_part_type::REGEXP;
11088
  // If regexp value is parser's segment wildcard regexp:
11089
493k
  if (regexp_value == segment_wildcard_regexp) {
11090
    // Set type to "segment-wildcard".
11091
1.77k
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11092
    // Set regexp value to the empty string.
11093
1.77k
    regexp_value.clear();
11094
492k
  } else if (regexp_value == ".*") {
11095
    // Otherwise if regexp value is the full wildcard regexp value:
11096
    // Set type to "full-wildcard".
11097
489k
    type = url_pattern_part_type::FULL_WILDCARD;
11098
    // Set regexp value to the empty string.
11099
489k
    regexp_value.clear();
11100
489k
  }
11101
  // Let name be the empty string.
11102
493k
  std::string name{};
11103
  // If name token is not null, then set name to name token's value.
11104
493k
  if (name_token) {
11105
1.76k
    name = name_token->value;
11106
492k
  } else if (regexp_or_wildcard_token != nullptr) {
11107
    // Otherwise if regexp or wildcard token is not null:
11108
    // Set name to parser's next numeric name, serialized.
11109
492k
    name = std::to_string(next_numeric_name);
11110
    // Increment parser's next numeric name by 1.
11111
492k
    next_numeric_name++;
11112
492k
  }
11113
  // If the result of running is a duplicate name given parser and name is
11114
  // true, then throw a TypeError.
11115
493k
  if (std::ranges::any_of(
11116
493k
          parts, [&name](const auto& part) { return part.name == name; })) {
11117
11
    return errors::type_error;
11118
11
  }
11119
  // Let encoded prefix be the result of running parser's encoding callback
11120
  // given prefix.
11121
493k
  auto encoded_prefix = encoding_callback(prefix);
11122
493k
  if (!encoded_prefix) return encoded_prefix.error();
11123
  // Let encoded suffix be the result of running parser's encoding callback
11124
  // given suffix.
11125
493k
  auto encoded_suffix = encoding_callback(suffix);
11126
493k
  if (!encoded_suffix) return encoded_suffix.error();
11127
  // Let part be a new part whose type is type, value is regexp value,
11128
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11129
  // is encoded suffix.
11130
  // Append part to parser's part list.
11131
493k
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11132
493k
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11133
493k
  return std::nullopt;
11134
493k
}
11135
11136
template <url_pattern_encoding_callback F>
11137
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11138
    std::string_view input, url_pattern_compile_component_options& options,
11139
922k
    F& encoding_callback) {
11140
922k
  ada_log("parse_pattern_string input=", input);
11141
  // Let parser be a new pattern parser whose encoding callback is encoding
11142
  // callback and segment wildcard regexp is the result of running generate a
11143
  // segment wildcard regexp given options.
11144
922k
  auto parser = url_pattern_parser<F>(
11145
922k
      encoding_callback, generate_segment_wildcard_regexp(options));
11146
  // Set parser's token list to the result of running tokenize given input and
11147
  // "strict".
11148
922k
  auto tokenize_result = tokenize(input, token_policy::strict);
11149
922k
  if (!tokenize_result) {
11150
1.19k
    ada_log("parse_pattern_string tokenize failed");
11151
1.19k
    return tl::unexpected(tokenize_result.error());
11152
1.19k
  }
11153
921k
  parser.tokens = std::move(*tokenize_result);
11154
11155
  // While parser's index is less than parser's token list's size:
11156
3.62M
  while (parser.can_continue()) {
11157
    // Let char token be the result of running try to consume a token given
11158
    // parser and "char".
11159
2.70M
    auto char_token = parser.try_consume_token(token_type::CHAR);
11160
    // Let name token be the result of running try to consume a token given
11161
    // parser and "name".
11162
2.70M
    auto name_token = parser.try_consume_token(token_type::NAME);
11163
    // Let regexp or wildcard token be the result of running try to consume a
11164
    // regexp or wildcard token given parser and name token.
11165
2.70M
    auto regexp_or_wildcard_token =
11166
2.70M
        parser.try_consume_regexp_or_wildcard_token(name_token);
11167
    // If name token is not null or regexp or wildcard token is not null:
11168
2.70M
    if (name_token || regexp_or_wildcard_token) {
11169
      // Let prefix be the empty string.
11170
493k
      std::string prefix{};
11171
      // If char token is not null then set prefix to char token's value.
11172
493k
      if (char_token) prefix = char_token->value;
11173
      // If prefix is not the empty string and not options's prefix code point:
11174
493k
      if (!prefix.empty() && prefix != options.get_prefix()) {
11175
        // Append prefix to the end of parser's pending fixed value.
11176
4.64k
        parser.pending_fixed_value.append(prefix);
11177
        // Set prefix to the empty string.
11178
4.64k
        prefix.clear();
11179
4.64k
      }
11180
      // Run maybe add a part from the pending fixed value given parser.
11181
493k
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11182
425
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11183
425
        return tl::unexpected(*error);
11184
425
      }
11185
      // Let modifier token be the result of running try to consume a modifier
11186
      // token given parser.
11187
492k
      auto modifier_token = parser.try_consume_modifier_token();
11188
      // Run add a part given parser, prefix, name token, regexp or wildcard
11189
      // token, the empty string, and modifier token.
11190
492k
      if (auto error =
11191
492k
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11192
492k
                              modifier_token)) {
11193
11
        ada_log("parser.add_part failed");
11194
11
        return tl::unexpected(*error);
11195
11
      }
11196
      // Continue.
11197
492k
      continue;
11198
492k
    }
11199
11200
    // Let fixed token be char token.
11201
2.21M
    auto fixed_token = char_token;
11202
    // If fixed token is null, then set fixed token to the result of running try
11203
    // to consume a token given parser and "escaped-char".
11204
2.21M
    if (!fixed_token)
11205
925k
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11206
    // If fixed token is not null:
11207
2.21M
    if (fixed_token) {
11208
      // Append fixed token's value to parser's pending fixed value.
11209
1.28M
      parser.pending_fixed_value.append(fixed_token->value);
11210
      // Continue.
11211
1.28M
      continue;
11212
1.28M
    }
11213
    // Let open token be the result of running try to consume a token given
11214
    // parser and "open".
11215
923k
    auto open_token = parser.try_consume_token(token_type::OPEN);
11216
    // If open token is not null:
11217
923k
    if (open_token) {
11218
      // Set prefix be the result of running consume text given parser.
11219
2.71k
      auto prefix_ = parser.consume_text();
11220
      // Set name token to the result of running try to consume a token given
11221
      // parser and "name".
11222
2.71k
      name_token = parser.try_consume_token(token_type::NAME);
11223
      // Set regexp or wildcard token to the result of running try to consume a
11224
      // regexp or wildcard token given parser and name token.
11225
2.71k
      regexp_or_wildcard_token =
11226
2.71k
          parser.try_consume_regexp_or_wildcard_token(name_token);
11227
      // Let suffix be the result of running consume text given parser.
11228
2.71k
      auto suffix_ = parser.consume_text();
11229
      // Run consume a required token given parser and "close".
11230
2.71k
      if (!parser.consume_required_token(token_type::CLOSE)) {
11231
136
        ada_log("parser.consume_required_token failed");
11232
136
        return tl::unexpected(errors::type_error);
11233
136
      }
11234
      // Set modifier token to the result of running try to consume a modifier
11235
      // token given parser.
11236
2.57k
      auto modifier_token = parser.try_consume_modifier_token();
11237
      // Run add a part given parser, prefix, name token, regexp or wildcard
11238
      // token, suffix, and modifier token.
11239
2.57k
      if (auto error =
11240
2.57k
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11241
2.57k
                              suffix_, modifier_token)) {
11242
84
        return tl::unexpected(*error);
11243
84
      }
11244
      // Continue.
11245
2.49k
      continue;
11246
2.57k
    }
11247
    // Run maybe add a part from the pending fixed value given parser.
11248
921k
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11249
2.93k
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11250
2.93k
      return tl::unexpected(*error);
11251
2.93k
    }
11252
    // Run consume a required token given parser and "end".
11253
918k
    if (!parser.consume_required_token(token_type::END)) {
11254
582
      return tl::unexpected(errors::type_error);
11255
582
    }
11256
918k
  }
11257
917k
  ada_log("parser.parts size is: ", parser.parts.size());
11258
  // Return parser's part list.
11259
917k
  return parser.parts;
11260
921k
}
11261
11262
template <url_pattern_regex::regex_concept regex_provider>
11263
bool protocol_component_matches_special_scheme(
11264
147k
    url_pattern_component<regex_provider>& component) {
11265
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11266
147k
  switch (component.type) {
11267
20.0k
    case url_pattern_component_type::EMPTY:
11268
      // Empty pattern can't match any special scheme
11269
20.0k
      return false;
11270
100k
    case url_pattern_component_type::EXACT_MATCH:
11271
      // Direct string comparison for exact match patterns
11272
100k
      return component.exact_match_value == "http" ||
11273
100k
             component.exact_match_value == "https" ||
11274
243
             component.exact_match_value == "ws" ||
11275
218
             component.exact_match_value == "wss" ||
11276
208
             component.exact_match_value == "ftp";
11277
24.8k
    case url_pattern_component_type::FULL_WILDCARD:
11278
      // Full wildcard matches everything including special schemes
11279
24.8k
      return true;
11280
1.23k
    case url_pattern_component_type::REGEXP:
11281
      // Fall back to regex matching for complex patterns
11282
1.23k
      auto& regex = component.regexp;
11283
1.23k
      return regex_provider::regex_match("http", regex) ||
11284
802
             regex_provider::regex_match("https", regex) ||
11285
760
             regex_provider::regex_match("ws", regex) ||
11286
728
             regex_provider::regex_match("wss", regex) ||
11287
707
             regex_provider::regex_match("ftp", regex);
11288
147k
  }
11289
0
  ada::unreachable();
11290
0
}
11291
11292
template <url_pattern_regex::regex_concept regex_provider>
11293
inline std::optional<errors> constructor_string_parser<
11294
38.8k
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11295
38.8k
  ada_log(
11296
38.8k
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11297
38.8k
      "flag");
11298
  // Let protocol string be the result of running make a component string given
11299
  // parser.
11300
38.8k
  auto protocol_string = make_component_string();
11301
  // Let protocol component be the result of compiling a component given
11302
  // protocol string, canonicalize a protocol, and default options.
11303
38.8k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11304
38.8k
      protocol_string, canonicalize_protocol,
11305
38.8k
      url_pattern_compile_component_options::DEFAULT);
11306
38.8k
  if (!protocol_component) {
11307
1.44k
    ada_log("url_pattern_component::compile failed for protocol_string ",
11308
1.44k
            protocol_string);
11309
1.44k
    return protocol_component.error();
11310
1.44k
  }
11311
  // If the result of running protocol component matches a special scheme given
11312
  // protocol component is true, then set parser's protocol matches a special
11313
  // scheme flag to true.
11314
37.4k
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11315
37.4k
    protocol_matches_a_special_scheme_flag = true;
11316
37.4k
  }
11317
37.4k
  return std::nullopt;
11318
38.8k
}
11319
11320
template <url_pattern_regex::regex_concept regex_provider>
11321
tl::expected<url_pattern_init, errors>
11322
74.8k
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11323
74.8k
  ada_log("constructor_string_parser::parse input=", input);
11324
  // Let parser be a new constructor string parser whose input is input and
11325
  // token list is the result of running tokenize given input and "lenient".
11326
74.8k
  auto token_list = tokenize(input, token_policy::lenient);
11327
74.8k
  if (!token_list) {
11328
0
    return tl::unexpected(token_list.error());
11329
0
  }
11330
74.8k
  auto parser = constructor_string_parser(input, std::move(*token_list));
11331
11332
  // While parser's token index is less than parser's token list size:
11333
1.75M
  while (parser.token_index < parser.token_list.size()) {
11334
    // Set parser's token increment to 1.
11335
1.75M
    parser.token_increment = 1;
11336
11337
    // If parser's token list[parser's token index]'s type is "end" then:
11338
1.75M
    if (parser.token_list[parser.token_index].type == token_type::END) {
11339
      // If parser's state is "init":
11340
109k
      if (parser.state == State::INIT) {
11341
        // Run rewind given parser.
11342
35.9k
        parser.rewind();
11343
        // If the result of running is a hash prefix given parser is true, then
11344
        // run change state given parser, "hash" and 1.
11345
35.9k
        if (parser.is_hash_prefix()) {
11346
0
          parser.change_state(State::HASH, 1);
11347
35.9k
        } else if (parser.is_search_prefix()) {
11348
          // Otherwise if the result of running is a search prefix given parser
11349
          // is true: Run change state given parser, "search" and 1.
11350
0
          parser.change_state(State::SEARCH, 1);
11351
35.9k
        } else {
11352
          // Run change state given parser, "pathname" and 0.
11353
35.9k
          parser.change_state(State::PATHNAME, 0);
11354
35.9k
        }
11355
        // Increment parser's token index by parser's token increment.
11356
35.9k
        parser.token_index += parser.token_increment;
11357
        // Continue.
11358
35.9k
        continue;
11359
35.9k
      }
11360
11361
73.3k
      if (parser.state == State::AUTHORITY) {
11362
        // If parser's state is "authority":
11363
        // Run rewind and set state given parser, and "hostname".
11364
0
        parser.rewind();
11365
0
        parser.change_state(State::HOSTNAME, 0);
11366
        // Increment parser's token index by parser's token increment.
11367
0
        parser.token_index += parser.token_increment;
11368
        // Continue.
11369
0
        continue;
11370
0
      }
11371
11372
      // Run change state given parser, "done" and 0.
11373
73.3k
      parser.change_state(State::DONE, 0);
11374
      // Break.
11375
73.3k
      break;
11376
73.3k
    }
11377
11378
    // If the result of running is a group open given parser is true:
11379
1.64M
    if (parser.is_group_open()) {
11380
      // Increment parser's group depth by 1.
11381
3.02k
      parser.group_depth += 1;
11382
      // Increment parser's token index by parser's token increment.
11383
3.02k
      parser.token_index += parser.token_increment;
11384
3.02k
    }
11385
11386
    // If parser's group depth is greater than 0:
11387
1.64M
    if (parser.group_depth > 0) {
11388
      // If the result of running is a group close given parser is true, then
11389
      // decrement parser's group depth by 1.
11390
12.9k
      if (parser.is_group_close()) {
11391
2.58k
        parser.group_depth -= 1;
11392
10.4k
      } else {
11393
        // Increment parser's token index by parser's token increment.
11394
10.4k
        parser.token_index += parser.token_increment;
11395
10.4k
        continue;
11396
10.4k
      }
11397
12.9k
    }
11398
11399
    // Switch on parser's state and run the associated steps:
11400
1.63M
    switch (parser.state) {
11401
331k
      case State::INIT: {
11402
        // If the result of running is a protocol suffix given parser is true:
11403
331k
        if (parser.is_protocol_suffix()) {
11404
          // Run rewind and set state given parser and "protocol".
11405
38.8k
          parser.rewind();
11406
38.8k
          parser.change_state(State::PROTOCOL, 0);
11407
38.8k
        }
11408
331k
        break;
11409
0
      }
11410
244k
      case State::PROTOCOL: {
11411
        // If the result of running is a protocol suffix given parser is true:
11412
244k
        if (parser.is_protocol_suffix()) {
11413
          // Run compute protocol matches a special scheme flag given parser.
11414
38.8k
          if (const auto error =
11415
38.8k
                  parser.compute_protocol_matches_special_scheme_flag()) {
11416
1.44k
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11417
1.44k
            return tl::unexpected(*error);
11418
1.44k
          }
11419
          // Let next state be "pathname".
11420
37.4k
          auto next_state = State::PATHNAME;
11421
          // Let skip be 1.
11422
37.4k
          auto skip = 1;
11423
          // If the result of running next is authority slashes given parser is
11424
          // true:
11425
37.4k
          if (parser.next_is_authority_slashes()) {
11426
            // Set next state to "authority".
11427
37.4k
            next_state = State::AUTHORITY;
11428
            // Set skip to 3.
11429
37.4k
            skip = 3;
11430
37.4k
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11431
            // Otherwise if parser's protocol matches a special scheme flag is
11432
            // true, then set next state to "authority".
11433
0
            next_state = State::AUTHORITY;
11434
0
          }
11435
11436
          // Run change state given parser, next state, and skip.
11437
37.4k
          parser.change_state(next_state, skip);
11438
37.4k
        }
11439
243k
        break;
11440
244k
      }
11441
448k
      case State::AUTHORITY: {
11442
        // If the result of running is an identity terminator given parser is
11443
        // true, then run rewind and set state given parser and "username".
11444
448k
        if (parser.is_an_identity_terminator()) {
11445
0
          parser.rewind();
11446
0
          parser.change_state(State::USERNAME, 0);
11447
448k
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11448
411k
                   parser.is_hash_prefix()) {
11449
          // Otherwise if any of the following are true:
11450
          // - the result of running is a pathname start given parser;
11451
          // - the result of running is a search prefix given parser; or
11452
          // - the result of running is a hash prefix given parser,
11453
          // then run rewind and set state given parser and "hostname".
11454
37.4k
          parser.rewind();
11455
37.4k
          parser.change_state(State::HOSTNAME, 0);
11456
37.4k
        }
11457
448k
        break;
11458
244k
      }
11459
0
      case State::USERNAME: {
11460
        // If the result of running is a password prefix given parser is true,
11461
        // then run change state given parser, "password", and 1.
11462
0
        if (parser.is_password_prefix()) {
11463
0
          parser.change_state(State::PASSWORD, 1);
11464
0
        } else if (parser.is_an_identity_terminator()) {
11465
          // Otherwise if the result of running is an identity terminator given
11466
          // parser is true, then run change state given parser, "hostname",
11467
          // and 1.
11468
0
          parser.change_state(State::HOSTNAME, 1);
11469
0
        }
11470
0
        break;
11471
244k
      }
11472
0
      case State::PASSWORD: {
11473
        // If the result of running is an identity terminator given parser is
11474
        // true, then run change state given parser, "hostname", and 1.
11475
0
        if (parser.is_an_identity_terminator()) {
11476
0
          parser.change_state(State::HOSTNAME, 1);
11477
0
        }
11478
0
        break;
11479
244k
      }
11480
448k
      case State::HOSTNAME: {
11481
        // If the result of running is an IPv6 open given parser is true, then
11482
        // increment parser's hostname IPv6 bracket depth by 1.
11483
448k
        if (parser.is_an_ipv6_open()) {
11484
0
          parser.hostname_ipv6_bracket_depth += 1;
11485
448k
        } else if (parser.is_an_ipv6_close()) {
11486
          // Otherwise if the result of running is an IPv6 close given parser is
11487
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11488
0
          parser.hostname_ipv6_bracket_depth -= 1;
11489
448k
        } else if (parser.is_port_prefix() &&
11490
0
                   parser.hostname_ipv6_bracket_depth == 0) {
11491
          // Otherwise if the result of running is a port prefix given parser is
11492
          // true and parser's hostname IPv6 bracket depth is zero, then run
11493
          // change state given parser, "port", and 1.
11494
0
          parser.change_state(State::PORT, 1);
11495
448k
        } else if (parser.is_pathname_start()) {
11496
          // Otherwise if the result of running is a pathname start given parser
11497
          // is true, then run change state given parser, "pathname", and 0.
11498
37.4k
          parser.change_state(State::PATHNAME, 0);
11499
411k
        } else if (parser.is_search_prefix()) {
11500
          // Otherwise if the result of running is a search prefix given parser
11501
          // is true, then run change state given parser, "search", and 1.
11502
0
          parser.change_state(State::SEARCH, 1);
11503
411k
        } else if (parser.is_hash_prefix()) {
11504
          // Otherwise if the result of running is a hash prefix given parser is
11505
          // true, then run change state given parser, "hash", and 1.
11506
0
          parser.change_state(State::HASH, 1);
11507
0
        }
11508
11509
448k
        break;
11510
244k
      }
11511
0
      case State::PORT: {
11512
        // If the result of running is a pathname start given parser is true,
11513
        // then run change state given parser, "pathname", and 0.
11514
0
        if (parser.is_pathname_start()) {
11515
0
          parser.change_state(State::PATHNAME, 0);
11516
0
        } else if (parser.is_search_prefix()) {
11517
          // Otherwise if the result of running is a search prefix given parser
11518
          // is true, then run change state given parser, "search", and 1.
11519
0
          parser.change_state(State::SEARCH, 1);
11520
0
        } else if (parser.is_hash_prefix()) {
11521
          // Otherwise if the result of running is a hash prefix given parser is
11522
          // true, then run change state given parser, "hash", and 1.
11523
0
          parser.change_state(State::HASH, 1);
11524
0
        }
11525
0
        break;
11526
244k
      }
11527
156k
      case State::PATHNAME: {
11528
        // If the result of running is a search prefix given parser is true,
11529
        // then run change state given parser, "search", and 1.
11530
156k
        if (parser.is_search_prefix()) {
11531
261
          parser.change_state(State::SEARCH, 1);
11532
156k
        } else if (parser.is_hash_prefix()) {
11533
          // Otherwise if the result of running is a hash prefix given parser is
11534
          // true, then run change state given parser, "hash", and 1.
11535
168
          parser.change_state(State::HASH, 1);
11536
168
        }
11537
156k
        break;
11538
244k
      }
11539
3.12k
      case State::SEARCH: {
11540
        // If the result of running is a hash prefix given parser is true, then
11541
        // run change state given parser, "hash", and 1.
11542
3.12k
        if (parser.is_hash_prefix()) {
11543
36
          parser.change_state(State::HASH, 1);
11544
36
        }
11545
3.12k
        break;
11546
244k
      }
11547
1.82k
      case State::HASH: {
11548
        // Do nothing
11549
1.82k
        break;
11550
244k
      }
11551
0
      default: {
11552
        // Assert: This step is never reached.
11553
0
        unreachable();
11554
0
      }
11555
1.63M
    }
11556
11557
    // Increment parser's token index by parser's token increment.
11558
1.63M
    parser.token_index += parser.token_increment;
11559
1.63M
  }
11560
11561
  // If parser's result contains "hostname" and not "port", then set parser's
11562
  // result["port"] to the empty string.
11563
73.3k
  if (parser.result.hostname && !parser.result.port) {
11564
37.4k
    parser.result.port = "";
11565
37.4k
  }
11566
11567
  // Return parser's result.
11568
73.3k
  return parser.result;
11569
74.8k
}
11570
11571
}  // namespace ada::url_pattern_helpers
11572
#endif  // ADA_INCLUDE_URL_PATTERN
11573
#endif
11574
/* end file include/ada/url_pattern_helpers-inl.h */
11575
11576
// Public API
11577
/* begin file include/ada/ada_version.h */
11578
/**
11579
 * @file ada_version.h
11580
 * @brief Definitions for Ada's version number.
11581
 */
11582
#ifndef ADA_ADA_VERSION_H
11583
#define ADA_ADA_VERSION_H
11584
11585
0
#define ADA_VERSION "3.4.4"
11586
11587
namespace ada {
11588
11589
enum {
11590
  ADA_VERSION_MAJOR = 3,
11591
  ADA_VERSION_MINOR = 4,
11592
  ADA_VERSION_REVISION = 4,
11593
};
11594
11595
}  // namespace ada
11596
11597
#endif  // ADA_ADA_VERSION_H
11598
/* end file include/ada/ada_version.h */
11599
/* begin file include/ada/implementation-inl.h */
11600
/**
11601
 * @file implementation-inl.h
11602
 */
11603
#ifndef ADA_IMPLEMENTATION_INL_H
11604
#define ADA_IMPLEMENTATION_INL_H
11605
11606
11607
11608
#include <variant>
11609
#include <string_view>
11610
11611
namespace ada {
11612
11613
#if ADA_INCLUDE_URL_PATTERN
11614
template <url_pattern_regex::regex_concept regex_provider>
11615
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
11616
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
11617
                  const std::string_view* base_url,
11618
199k
                  const url_pattern_options* options) {
11619
199k
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
11620
199k
                                                        base_url, options);
11621
199k
}
11622
#endif  // ADA_INCLUDE_URL_PATTERN
11623
11624
}  // namespace ada
11625
11626
#endif  // ADA_IMPLEMENTATION_INL_H
11627
/* end file include/ada/implementation-inl.h */
11628
11629
#endif  // ADA_H
11630
/* end file include/ada.h */