Coverage Report

Created: 2026-10-17 07:11

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-10-15 09:03:13 -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-10-09 11:29:39 -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 non-ASCII to_ascii input and for
154
// to_unicode input.
155
// Bounds heap growth under untrusted input (DoS resistance). DNS wire limits
156
// are smaller; this allows long Unicode labels used in URL tests/fixtures.
157
inline constexpr size_t max_domain_input_bytes = 16384;
158
159
}  // namespace ada::idna
160
161
#endif  // ADA_IDNA_LIMITS_H
162
/* end file include/ada/idna/limits.h */
163
164
namespace ada::idna {
165
166
// Converts a domain (e.g., www.google.com) possibly containing international
167
// characters to an ascii domain (with punycode). It will not do percent
168
// decoding: percent decoding should be done prior to calling this function. We
169
// do not remove tabs and spaces, they should have been removed prior to calling
170
// this function. We also do not trim control characters. We also assume that
171
// the input is not empty. We return "" on error. Non-ASCII inputs longer than
172
// max_domain_input_bytes are rejected; ASCII inputs of any length are accepted
173
// (they are only lowercased).
174
//
175
// This function may accept or even produce invalid domains (WHATWG carve-outs).
176
std::string to_ascii(std::string_view ut8_string);
177
178
// Same as to_ascii, but writes into `out` and returns false on error without
179
// relying on empty-string ambiguity.
180
[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out);
181
182
// Returns true if the string contains a forbidden code point according to the
183
// WHATGL URL specification:
184
// https://url.spec.whatwg.org/#forbidden-domain-code-point
185
bool contains_forbidden_domain_code_point(std::string_view ascii_string);
186
187
bool constexpr is_ascii(std::u32string_view view);
188
bool constexpr is_ascii(std::string_view view);
189
190
}  // namespace ada::idna
191
192
#endif  // ADA_IDNA_TO_ASCII_H
193
/* end file include/ada/idna/to_ascii.h */
194
/* begin file include/ada/idna/to_unicode.h */
195
#ifndef ADA_IDNA_TO_UNICODE_H
196
#define ADA_IDNA_TO_UNICODE_H
197
198
#include <string>
199
#include <string_view>
200
201
namespace ada::idna {
202
203
// UTS #46 ToUnicode. Never fails per the standard: on step failure the original
204
// label is kept. Inputs longer than max_domain_input_bytes are returned
205
// unchanged as a safety measure under untrusted input.
206
std::string to_unicode(std::string_view input);
207
208
// Writes into `out`. Returns false only if the input exceeds
209
// max_domain_input_bytes (out is left empty). Otherwise always returns true
210
// (ToUnicode does not fail).
211
[[nodiscard]] bool to_unicode(std::string_view input, std::string& out);
212
213
}  // namespace ada::idna
214
215
#endif  // ADA_IDNA_TO_UNICODE_H
216
/* end file include/ada/idna/to_unicode.h */
217
/* begin file include/ada/idna/identifier.h */
218
#ifndef ADA_IDNA_IDENTIFIER_H
219
#define ADA_IDNA_IDENTIFIER_H
220
221
#include <string>
222
#include <string_view>
223
224
namespace ada::idna {
225
226
// Verify if it is valid name code point given a Unicode code point and a
227
// boolean first: If first is true return the result of checking if code point
228
// is contained in the IdentifierStart set of code points. Otherwise return the
229
// result of checking if code point is contained in the IdentifierPart set of
230
// code points. Returns false if the input is empty or the code point is not
231
// valid. There is minimal Unicode error handling: the input should be valid
232
// UTF-8. https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
233
bool valid_name_code_point(char32_t code_point, bool first);
234
235
}  // namespace ada::idna
236
237
#endif
238
/* end file include/ada/idna/identifier.h */
239
240
#endif
241
/* end file include/idna.h */
242
/* end file include/ada/ada_idna.h */
243
/* begin file include/ada/character_sets.h */
244
/**
245
 * @file character_sets.h
246
 * @brief Declaration of the character sets used by unicode functions.
247
 * @author Node.js
248
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
249
 */
250
#ifndef ADA_CHARACTER_SETS_H
251
#define ADA_CHARACTER_SETS_H
252
253
/* begin file include/ada/common_defs.h */
254
/**
255
 * @file common_defs.h
256
 * @brief Cross-platform compiler macros and common definitions.
257
 *
258
 * This header provides compiler-specific macros for optimization hints,
259
 * platform detection, SIMD support detection, and development/debug utilities.
260
 * It ensures consistent behavior across different compilers (GCC, Clang, MSVC).
261
 */
262
#ifndef ADA_COMMON_DEFS_H
263
#define ADA_COMMON_DEFS_H
264
265
// https://en.cppreference.com/w/cpp/feature_test#Library_features
266
// detect C++20 features
267
#include <version>
268
269
#ifdef _MSC_VER
270
#define ADA_VISUAL_STUDIO 1
271
/**
272
 * We want to differentiate carefully between
273
 * clang under visual studio and regular visual
274
 * studio.
275
 */
276
#ifdef __clang__
277
// clang under visual studio
278
#define ADA_CLANG_VISUAL_STUDIO 1
279
#else
280
// just regular visual studio (best guess)
281
#define ADA_REGULAR_VISUAL_STUDIO 1
282
#endif  // __clang__
283
#endif  // _MSC_VER
284
285
#if defined(__GNUC__)
286
// Marks a block with a name so that MCA analysis can see it.
287
#define ADA_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
288
#define ADA_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
289
#define ADA_DEBUG_BLOCK(name, block) \
290
  BEGIN_DEBUG_BLOCK(name);           \
291
  block;                             \
292
  END_DEBUG_BLOCK(name);
293
#else
294
#define ADA_BEGIN_DEBUG_BLOCK(name)
295
#define ADA_END_DEBUG_BLOCK(name)
296
#define ADA_DEBUG_BLOCK(name, block)
297
#endif
298
299
// Align to N-byte boundary
300
#define ADA_ROUNDUP_N(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
301
#define ADA_ROUNDDOWN_N(a, n) ((a) & ~((n) - 1))
302
303
#define ADA_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n) - 1)) == 0)
304
305
#if defined(ADA_REGULAR_VISUAL_STUDIO)
306
307
#define ada_really_inline __forceinline
308
#define ada_never_inline __declspec(noinline)
309
310
#define ada_unused
311
#define ada_warn_unused
312
313
#define ADA_PUSH_DISABLE_WARNINGS __pragma(warning(push))
314
#define ADA_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
315
#define ADA_DISABLE_VS_WARNING(WARNING_NUMBER) \
316
  __pragma(warning(disable : WARNING_NUMBER))
317
// Get rid of Intellisense-only warnings (Code Analysis)
318
// Though __has_include is C++17, it is supported in Visual Studio 2017 or
319
// better (_MSC_VER>=1910).
320
#ifdef __has_include
321
#if __has_include(<CppCoreCheck\Warnings.h>)
322
#include <CppCoreCheck\Warnings.h>
323
#define ADA_DISABLE_UNDESIRED_WARNINGS \
324
  ADA_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
325
#endif
326
#endif
327
328
#ifndef ADA_DISABLE_UNDESIRED_WARNINGS
329
#define ADA_DISABLE_UNDESIRED_WARNINGS
330
#endif
331
332
#define ADA_DISABLE_DEPRECATED_WARNING ADA_DISABLE_VS_WARNING(4996)
333
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING
334
#define ADA_POP_DISABLE_WARNINGS __pragma(warning(pop))
335
336
#else  // ADA_REGULAR_VISUAL_STUDIO
337
338
#define ada_really_inline inline __attribute__((always_inline))
339
#define ada_never_inline inline __attribute__((noinline))
340
341
#define ada_unused __attribute__((unused))
342
#define ada_warn_unused __attribute__((warn_unused_result))
343
344
#define ADA_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
345
// gcc doesn't seem to disable all warnings with all and extra, add warnings
346
// here as necessary
347
#define ADA_PUSH_DISABLE_ALL_WARNINGS               \
348
  ADA_PUSH_DISABLE_WARNINGS                         \
349
  ADA_DISABLE_GCC_WARNING("-Weffc++")               \
350
  ADA_DISABLE_GCC_WARNING("-Wall")                  \
351
  ADA_DISABLE_GCC_WARNING("-Wconversion")           \
352
  ADA_DISABLE_GCC_WARNING("-Wextra")                \
353
  ADA_DISABLE_GCC_WARNING("-Wattributes")           \
354
  ADA_DISABLE_GCC_WARNING("-Wimplicit-fallthrough") \
355
  ADA_DISABLE_GCC_WARNING("-Wnon-virtual-dtor")     \
356
  ADA_DISABLE_GCC_WARNING("-Wreturn-type")          \
357
  ADA_DISABLE_GCC_WARNING("-Wshadow")               \
358
  ADA_DISABLE_GCC_WARNING("-Wunused-parameter")     \
359
  ADA_DISABLE_GCC_WARNING("-Wunused-variable")      \
360
  ADA_DISABLE_GCC_WARNING("-Wsign-compare")
361
#define ADA_PRAGMA(P) _Pragma(#P)
362
#define ADA_DISABLE_GCC_WARNING(WARNING) \
363
  ADA_PRAGMA(GCC diagnostic ignored WARNING)
364
#if defined(ADA_CLANG_VISUAL_STUDIO)
365
#define ADA_DISABLE_UNDESIRED_WARNINGS \
366
  ADA_DISABLE_GCC_WARNING("-Wmicrosoft-include")
367
#else
368
#define ADA_DISABLE_UNDESIRED_WARNINGS
369
#endif
370
#define ADA_DISABLE_DEPRECATED_WARNING \
371
  ADA_DISABLE_GCC_WARNING("-Wdeprecated-declarations")
372
#define ADA_DISABLE_STRICT_OVERFLOW_WARNING \
373
  ADA_DISABLE_GCC_WARNING("-Wstrict-overflow")
374
#define ADA_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
375
376
#endif  // MSC_VER
377
378
#if defined(ADA_VISUAL_STUDIO)
379
/**
380
 * It does not matter here whether you are using
381
 * the regular visual studio or clang under visual
382
 * studio.
383
 */
384
#if ADA_USING_LIBRARY
385
#define ADA_DLLIMPORTEXPORT __declspec(dllimport)
386
#else
387
#define ADA_DLLIMPORTEXPORT __declspec(dllexport)
388
#endif
389
#else
390
#define ADA_DLLIMPORTEXPORT
391
#endif
392
393
/// If EXPR is an error, returns it.
394
#define ADA_TRY(EXPR)   \
395
  {                     \
396
    auto _err = (EXPR); \
397
    if (_err) {         \
398
      return _err;      \
399
    }                   \
400
  }
401
402
// __has_cpp_attribute is part of C++20
403
#if !defined(__has_cpp_attribute)
404
#define __has_cpp_attribute(x) 0
405
#endif
406
407
#if __has_cpp_attribute(gnu::noinline)
408
#define ADA_ATTRIBUTE_NOINLINE [[gnu::noinline]]
409
#else
410
#define ADA_ATTRIBUTE_NOINLINE
411
#endif
412
413
namespace ada {
414
0
[[noreturn]] inline void unreachable() {
415
0
#ifdef __GNUC__
416
0
  __builtin_unreachable();
417
#elif defined(_MSC_VER)
418
  __assume(false);
419
#else
420
#endif
421
0
}
422
}  // namespace ada
423
424
// Unless the programmer has already set ADA_DEVELOPMENT_CHECKS,
425
// we want to set it under debug builds. We detect a debug build
426
// under Visual Studio when the _DEBUG macro is set. Under the other
427
// compilers, we use the fact that they define __OPTIMIZE__ whenever
428
// they allow optimizations.
429
// It is possible that this could miss some cases where ADA_DEVELOPMENT_CHECKS
430
// is helpful, but the programmer can set the macro ADA_DEVELOPMENT_CHECKS.
431
// It could also wrongly set ADA_DEVELOPMENT_CHECKS (e.g., if the programmer
432
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails
433
// to set the __OPTIMIZE__ macro).
434
#if !defined(ADA_DEVELOPMENT_CHECKS) && !defined(NDEBUG)
435
#ifdef _MSC_VER
436
// Visual Studio seems to set _DEBUG for debug builds.
437
#ifdef _DEBUG
438
#define ADA_DEVELOPMENT_CHECKS 1
439
#endif  // _DEBUG
440
#else   // _MSC_VER
441
// All other compilers appear to set __OPTIMIZE__ to a positive integer
442
// when the compiler is optimizing.
443
#ifndef __OPTIMIZE__
444
#define ADA_DEVELOPMENT_CHECKS 1
445
#endif  // __OPTIMIZE__
446
#endif  // _MSC_VER
447
#endif  // ADA_DEVELOPMENT_CHECKS
448
449
#define ADA_STR(x) #x
450
451
#if ADA_DEVELOPMENT_CHECKS
452
#define ADA_REQUIRE(EXPR) \
453
  {                       \
454
    if (!(EXPR) { abort(); }) }
455
456
#define ADA_FAIL(MESSAGE)                            \
457
  do {                                               \
458
    std::cerr << "FAIL: " << (MESSAGE) << std::endl; \
459
    abort();                                         \
460
  } while (0);
461
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)                                    \
462
  do {                                                                         \
463
    if (LHS != RHS) {                                                          \
464
      std::cerr << "Mismatch: '" << LHS << "' - '" << RHS << "'" << std::endl; \
465
      ADA_FAIL(MESSAGE);                                                       \
466
    }                                                                          \
467
  } while (0);
468
#define ADA_ASSERT_TRUE(COND)                                               \
469
  do {                                                                      \
470
    if (!(COND)) {                                                          \
471
      std::cerr << "Assert at line " << __LINE__ << " of file " << __FILE__ \
472
                << std::endl;                                               \
473
      ADA_FAIL(ADA_STR(COND));                                              \
474
    }                                                                       \
475
  } while (0);
476
#else
477
#define ADA_FAIL(MESSAGE)
478
#define ADA_ASSERT_EQUAL(LHS, RHS, MESSAGE)
479
#define ADA_ASSERT_TRUE(COND)
480
#endif
481
482
#ifdef ADA_VISUAL_STUDIO
483
#define ADA_ASSUME(COND) __assume(COND)
484
#else
485
#define ADA_ASSUME(COND)       \
486
  do {                         \
487
    if (!(COND)) {             \
488
      __builtin_unreachable(); \
489
    }                          \
490
  } while (0)
491
#endif
492
493
#if defined(__SSSE3__)
494
#define ADA_SSSE3 1
495
#endif
496
497
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
498
    (defined(_M_AMD64) || defined(_M_X64) ||                         \
499
     (defined(_M_IX86_FP) && _M_IX86_FP == 2))
500
#define ADA_SSE2 1
501
#endif
502
503
// AVX-512 byte/word ops + 128/256-bit vectors of AVX-512 instructions.
504
// Used for optional high-performance IP address parsing kernels.
505
#if defined(__AVX512BW__) && defined(__AVX512VL__)
506
#define ADA_AVX512 1
507
#endif
508
509
#if defined(__aarch64__) || defined(_M_ARM64)
510
#define ADA_NEON 1
511
#endif
512
513
#if defined(__loongarch_sx)
514
#define ADA_LSX 1
515
#endif
516
517
#if defined(__riscv_v) && __riscv_v_intrinsic >= 11000
518
// Support RVV intrinsics v0.11 and above
519
#define ADA_RVV 1
520
#endif
521
522
#ifndef __has_cpp_attribute
523
#define ada_lifetime_bound
524
#elif __has_cpp_attribute(msvc::lifetimebound)
525
#define ada_lifetime_bound [[msvc::lifetimebound]]
526
#elif __has_cpp_attribute(clang::lifetimebound)
527
#define ada_lifetime_bound [[clang::lifetimebound]]
528
#elif __has_cpp_attribute(lifetimebound)
529
#define ada_lifetime_bound [[lifetimebound]]
530
#else
531
#define ada_lifetime_bound
532
#endif
533
534
#ifdef __cpp_lib_format
535
#if __cpp_lib_format >= 202110L
536
#include <format>
537
#define ADA_HAS_FORMAT 1
538
#endif
539
#endif
540
541
#ifndef ADA_INCLUDE_URL_PATTERN
542
#define ADA_INCLUDE_URL_PATTERN 1
543
#endif  // ADA_INCLUDE_URL_PATTERN
544
545
// When set, NEON all-zero tests use an integer compare instead of
546
// reinterpreting the bits as a double. The double compare is faster (no
547
// GPR transfer) but is incorrect if flush-to-zero is enabled.
548
#ifndef ADA_NEON_SAFE_ZERO_CHECK
549
#define ADA_NEON_SAFE_ZERO_CHECK 1
550
#endif  // ADA_NEON_SAFE_ZERO_CHECK
551
552
#endif  // ADA_COMMON_DEFS_H
553
/* end file include/ada/common_defs.h */
554
#include <cstdint>
555
556
/**
557
 * These functions are not part of our public API and may
558
 * change at any time.
559
 * @private
560
 * @namespace ada::character_sets
561
 * @brief Includes the definitions for unicode character sets.
562
 */
563
namespace ada::character_sets {
564
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
565
}  // namespace ada::character_sets
566
567
#endif  // ADA_CHARACTER_SETS_H
568
/* end file include/ada/character_sets.h */
569
/* begin file include/ada/character_sets-inl.h */
570
/**
571
 * @file character_sets-inl.h
572
 * @brief Definitions of the character sets used by unicode functions.
573
 * @author Node.js
574
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
575
 */
576
#ifndef ADA_CHARACTER_SETS_INL_H
577
#define ADA_CHARACTER_SETS_INL_H
578
579
580
#include <cstdint>
581
582
/**
583
 * These functions are not part of our public API and may
584
 * change at any time.
585
 * @private
586
 */
587
namespace ada::character_sets {
588
589
constexpr char hex[1024] =
590
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
591
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
592
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
593
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
594
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
595
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
596
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
597
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
598
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
599
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
600
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
601
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
602
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
603
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
604
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
605
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
606
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
607
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
608
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
609
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
610
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
611
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
612
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
613
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
614
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
615
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
616
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
617
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
618
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
619
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
620
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
621
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
622
623
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
624
    // 00     01     02     03     04     05     06     07
625
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
626
    // 08     09     0A     0B     0C     0D     0E     0F
627
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
628
    // 10     11     12     13     14     15     16     17
629
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
630
    // 18     19     1A     1B     1C     1D     1E     1F
631
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
632
    // 20     21     22     23     24     25     26     27
633
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
634
    // 28     29     2A     2B     2C     2D     2E     2F
635
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
636
    // 30     31     32     33     34     35     36     37
637
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
638
    // 38     39     3A     3B     3C     3D     3E     3F
639
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
640
    // 40     41     42     43     44     45     46     47
641
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
642
    // 48     49     4A     4B     4C     4D     4E     4F
643
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
644
    // 50     51     52     53     54     55     56     57
645
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
646
    // 58     59     5A     5B     5C     5D     5E     5F
647
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
648
    // 60     61     62     63     64     65     66     67
649
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
650
    // 68     69     6A     6B     6C     6D     6E     6F
651
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
652
    // 70     71     72     73     74     75     76     77
653
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
654
    // 78     79     7A     7B     7C     7D     7E     7F
655
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
656
    // 80     81     82     83     84     85     86     87
657
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
658
    // 88     89     8A     8B     8C     8D     8E     8F
659
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
660
    // 90     91     92     93     94     95     96     97
661
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
662
    // 98     99     9A     9B     9C     9D     9E     9F
663
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
664
    // A0     A1     A2     A3     A4     A5     A6     A7
665
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
666
    // A8     A9     AA     AB     AC     AD     AE     AF
667
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
668
    // B0     B1     B2     B3     B4     B5     B6     B7
669
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
670
    // B8     B9     BA     BB     BC     BD     BE     BF
671
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
672
    // C0     C1     C2     C3     C4     C5     C6     C7
673
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
674
    // C8     C9     CA     CB     CC     CD     CE     CF
675
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
676
    // D0     D1     D2     D3     D4     D5     D6     D7
677
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
678
    // D8     D9     DA     DB     DC     DD     DE     DF
679
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
680
    // E0     E1     E2     E3     E4     E5     E6     E7
681
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
682
    // E8     E9     EA     EB     EC     ED     EE     EF
683
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
684
    // F0     F1     F2     F3     F4     F5     F6     F7
685
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
686
    // F8     F9     FA     FB     FC     FD     FE     FF
687
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
688
689
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
690
    // 00     01     02     03     04     05     06     07
691
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
692
    // 08     09     0A     0B     0C     0D     0E     0F
693
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
694
    // 10     11     12     13     14     15     16     17
695
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
696
    // 18     19     1A     1B     1C     1D     1E     1F
697
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
698
    // 20     21     22     23     24     25     26     27
699
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
700
    // 28     29     2A     2B     2C     2D     2E     2F
701
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
702
    // 30     31     32     33     34     35     36     37
703
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
704
    // 38     39     3A     3B     3C     3D     3E     3F
705
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
706
    // 40     41     42     43     44     45     46     47
707
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
708
    // 48     49     4A     4B     4C     4D     4E     4F
709
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
710
    // 50     51     52     53     54     55     56     57
711
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
712
    // 58     59     5A     5B     5C     5D     5E     5F
713
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
714
    // 60     61     62     63     64     65     66     67
715
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
716
    // 68     69     6A     6B     6C     6D     6E     6F
717
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
718
    // 70     71     72     73     74     75     76     77
719
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
720
    // 78     79     7A     7B     7C     7D     7E     7F
721
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
722
    // 80     81     82     83     84     85     86     87
723
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
724
    // 88     89     8A     8B     8C     8D     8E     8F
725
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
726
    // 90     91     92     93     94     95     96     97
727
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
728
    // 98     99     9A     9B     9C     9D     9E     9F
729
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
730
    // A0     A1     A2     A3     A4     A5     A6     A7
731
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
732
    // A8     A9     AA     AB     AC     AD     AE     AF
733
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
734
    // B0     B1     B2     B3     B4     B5     B6     B7
735
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
736
    // B8     B9     BA     BB     BC     BD     BE     BF
737
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
738
    // C0     C1     C2     C3     C4     C5     C6     C7
739
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
740
    // C8     C9     CA     CB     CC     CD     CE     CF
741
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
742
    // D0     D1     D2     D3     D4     D5     D6     D7
743
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
744
    // D8     D9     DA     DB     DC     DD     DE     DF
745
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
746
    // E0     E1     E2     E3     E4     E5     E6     E7
747
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
748
    // E8     E9     EA     EB     EC     ED     EE     EF
749
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
750
    // F0     F1     F2     F3     F4     F5     F6     F7
751
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
752
    // F8     F9     FA     FB     FC     FD     FE     FF
753
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
754
755
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
756
    // 00     01     02     03     04     05     06     07
757
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
758
    // 08     09     0A     0B     0C     0D     0E     0F
759
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
760
    // 10     11     12     13     14     15     16     17
761
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
762
    // 18     19     1A     1B     1C     1D     1E     1F
763
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
764
    // 20     21     22     23     24     25     26     27
765
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
766
    // 28     29     2A     2B     2C     2D     2E     2F
767
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
768
    // 30     31     32     33     34     35     36     37
769
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
770
    // 38     39     3A     3B     3C     3D     3E     3F
771
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
772
    // 40     41     42     43     44     45     46     47
773
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
774
    // 48     49     4A     4B     4C     4D     4E     4F
775
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
776
    // 50     51     52     53     54     55     56     57
777
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
778
    // 58     59     5A     5B     5C     5D     5E     5F
779
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
780
    // 60     61     62     63     64     65     66     67
781
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
782
    // 68     69     6A     6B     6C     6D     6E     6F
783
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
784
    // 70     71     72     73     74     75     76     77
785
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
786
    // 78     79     7A     7B     7C     7D     7E     7F
787
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
788
    // 80     81     82     83     84     85     86     87
789
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
790
    // 88     89     8A     8B     8C     8D     8E     8F
791
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
792
    // 90     91     92     93     94     95     96     97
793
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
794
    // 98     99     9A     9B     9C     9D     9E     9F
795
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
796
    // A0     A1     A2     A3     A4     A5     A6     A7
797
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
798
    // A8     A9     AA     AB     AC     AD     AE     AF
799
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
800
    // B0     B1     B2     B3     B4     B5     B6     B7
801
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
802
    // B8     B9     BA     BB     BC     BD     BE     BF
803
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
804
    // C0     C1     C2     C3     C4     C5     C6     C7
805
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
806
    // C8     C9     CA     CB     CC     CD     CE     CF
807
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
808
    // D0     D1     D2     D3     D4     D5     D6     D7
809
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
810
    // D8     D9     DA     DB     DC     DD     DE     DF
811
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
812
    // E0     E1     E2     E3     E4     E5     E6     E7
813
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
814
    // E8     E9     EA     EB     EC     ED     EE     EF
815
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
816
    // F0     F1     F2     F3     F4     F5     F6     F7
817
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
818
    // F8     F9     FA     FB     FC     FD     FE     FF
819
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
820
821
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
822
    // 00     01     02     03     04     05     06     07
823
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
824
    // 08     09     0A     0B     0C     0D     0E     0F
825
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
826
    // 10     11     12     13     14     15     16     17
827
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
828
    // 18     19     1A     1B     1C     1D     1E     1F
829
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
830
    // 20     21     22     23     24     25     26     27
831
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
832
    // 28     29     2A     2B     2C     2D     2E     2F
833
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
834
    // 30     31     32     33     34     35     36     37
835
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
836
    // 38     39     3A     3B     3C     3D     3E     3F
837
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
838
    // 40     41     42     43     44     45     46     47
839
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
840
    // 48     49     4A     4B     4C     4D     4E     4F
841
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
842
    // 50     51     52     53     54     55     56     57
843
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
844
    // 58     59     5A     5B     5C     5D     5E     5F
845
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
846
    // 60     61     62     63     64     65     66     67
847
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
848
    // 68     69     6A     6B     6C     6D     6E     6F
849
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
850
    // 70     71     72     73     74     75     76     77
851
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
852
    // 78     79     7A     7B     7C     7D     7E     7F
853
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
854
    // 80     81     82     83     84     85     86     87
855
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
856
    // 88     89     8A     8B     8C     8D     8E     8F
857
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
858
    // 90     91     92     93     94     95     96     97
859
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
860
    // 98     99     9A     9B     9C     9D     9E     9F
861
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
862
    // A0     A1     A2     A3     A4     A5     A6     A7
863
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
864
    // A8     A9     AA     AB     AC     AD     AE     AF
865
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
866
    // B0     B1     B2     B3     B4     B5     B6     B7
867
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
868
    // B8     B9     BA     BB     BC     BD     BE     BF
869
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
870
    // C0     C1     C2     C3     C4     C5     C6     C7
871
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
872
    // C8     C9     CA     CB     CC     CD     CE     CF
873
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
874
    // D0     D1     D2     D3     D4     D5     D6     D7
875
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
876
    // D8     D9     DA     DB     DC     DD     DE     DF
877
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
878
    // E0     E1     E2     E3     E4     E5     E6     E7
879
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
880
    // E8     E9     EA     EB     EC     ED     EE     EF
881
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
882
    // F0     F1     F2     F3     F4     F5     F6     F7
883
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
884
    // F8     F9     FA     FB     FC     FD     FE     FF
885
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
886
887
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
888
    // 00     01     02     03     04     05     06     07
889
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
890
    // 08     09     0A     0B     0C     0D     0E     0F
891
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
892
    // 10     11     12     13     14     15     16     17
893
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
894
    // 18     19     1A     1B     1C     1D     1E     1F
895
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
896
    // 20     21     22     23     24     25     26     27
897
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
898
    // 28     29     2A     2B     2C     2D     2E     2F
899
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
900
    // 30     31     32     33     34     35     36     37
901
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
902
    // 38     39     3A     3B     3C     3D     3E     3F
903
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
904
    // 40     41     42     43     44     45     46     47
905
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
906
    // 48     49     4A     4B     4C     4D     4E     4F
907
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
908
    // 50     51     52     53     54     55     56     57
909
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
910
    // 58     59     5A     5B     5C     5D     5E     5F
911
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
912
    // 60     61     62     63     64     65     66     67
913
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
914
    // 68     69     6A     6B     6C     6D     6E     6F
915
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
916
    // 70     71     72     73     74     75     76     77
917
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
918
    // 78     79     7A     7B     7C     7D     7E     7F
919
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
920
    // 80     81     82     83     84     85     86     87
921
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
922
    // 88     89     8A     8B     8C     8D     8E     8F
923
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
924
    // 90     91     92     93     94     95     96     97
925
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
926
    // 98     99     9A     9B     9C     9D     9E     9F
927
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
928
    // A0     A1     A2     A3     A4     A5     A6     A7
929
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
930
    // A8     A9     AA     AB     AC     AD     AE     AF
931
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
932
    // B0     B1     B2     B3     B4     B5     B6     B7
933
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
934
    // B8     B9     BA     BB     BC     BD     BE     BF
935
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
936
    // C0     C1     C2     C3     C4     C5     C6     C7
937
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
938
    // C8     C9     CA     CB     CC     CD     CE     CF
939
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
940
    // D0     D1     D2     D3     D4     D5     D6     D7
941
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
942
    // D8     D9     DA     DB     DC     DD     DE     DF
943
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
944
    // E0     E1     E2     E3     E4     E5     E6     E7
945
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
946
    // E8     E9     EA     EB     EC     ED     EE     EF
947
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
948
    // F0     F1     F2     F3     F4     F5     F6     F7
949
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
950
    // F8     F9     FA     FB     FC     FD     FE     FF
951
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
952
953
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
954
    // 00     01     02     03     04     05     06     07
955
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
956
    // 08     09     0A     0B     0C     0D     0E     0F
957
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
958
    // 10     11     12     13     14     15     16     17
959
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
960
    // 18     19     1A     1B     1C     1D     1E     1F
961
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
962
    // 20     21     22     23     24     25     26     27
963
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
964
    // 28     29     2A     2B     2C     2D     2E     2F
965
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
966
    // 30     31     32     33     34     35     36     37
967
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
968
    // 38     39     3A     3B     3C     3D     3E     3F
969
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
970
    // 40     41     42     43     44     45     46     47
971
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
972
    // 48     49     4A     4B     4C     4D     4E     4F
973
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
974
    // 50     51     52     53     54     55     56     57
975
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
976
    // 58     59     5A     5B     5C     5D     5E     5F
977
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
978
    // 60     61     62     63     64     65     66     67
979
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
980
    // 68     69     6A     6B     6C     6D     6E     6F
981
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
982
    // 70     71     72     73     74     75     76     77
983
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
984
    // 78     79     7A     7B     7C     7D     7E     7F
985
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
986
    // 80     81     82     83     84     85     86     87
987
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
988
    // 88     89     8A     8B     8C     8D     8E     8F
989
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
990
    // 90     91     92     93     94     95     96     97
991
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
992
    // 98     99     9A     9B     9C     9D     9E     9F
993
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
994
    // A0     A1     A2     A3     A4     A5     A6     A7
995
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
996
    // A8     A9     AA     AB     AC     AD     AE     AF
997
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
998
    // B0     B1     B2     B3     B4     B5     B6     B7
999
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1000
    // B8     B9     BA     BB     BC     BD     BE     BF
1001
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1002
    // C0     C1     C2     C3     C4     C5     C6     C7
1003
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1004
    // C8     C9     CA     CB     CC     CD     CE     CF
1005
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1006
    // D0     D1     D2     D3     D4     D5     D6     D7
1007
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1008
    // D8     D9     DA     DB     DC     DD     DE     DF
1009
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1010
    // E0     E1     E2     E3     E4     E5     E6     E7
1011
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1012
    // E8     E9     EA     EB     EC     ED     EE     EF
1013
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1014
    // F0     F1     F2     F3     F4     F5     F6     F7
1015
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1016
    // F8     F9     FA     FB     FC     FD     FE     FF
1017
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1018
1019
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1020
    // 00     01     02     03     04     05     06     07
1021
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1022
    // 08     09     0A     0B     0C     0D     0E     0F
1023
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1024
    // 10     11     12     13     14     15     16     17
1025
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1026
    // 18     19     1A     1B     1C     1D     1E     1F
1027
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1028
    // 20     21     22     23     24     25     26     27
1029
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1030
    // 28     29     2A     2B     2C     2D     2E     2F
1031
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1032
    // 30     31     32     33     34     35     36     37
1033
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1034
    // 38     39     3A     3B     3C     3D     3E     3F
1035
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1036
    // 40     41     42     43     44     45     46     47
1037
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1038
    // 48     49     4A     4B     4C     4D     4E     4F
1039
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1040
    // 50     51     52     53     54     55     56     57
1041
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1042
    // 58     59     5A     5B     5C     5D     5E     5F
1043
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1044
    // 60     61     62     63     64     65     66     67
1045
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1046
    // 68     69     6A     6B     6C     6D     6E     6F
1047
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1048
    // 70     71     72     73     74     75     76     77
1049
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1050
    // 78     79     7A     7B     7C     7D     7E     7F
1051
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1052
    // 80     81     82     83     84     85     86     87
1053
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1054
    // 88     89     8A     8B     8C     8D     8E     8F
1055
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1056
    // 90     91     92     93     94     95     96     97
1057
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1058
    // 98     99     9A     9B     9C     9D     9E     9F
1059
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1060
    // A0     A1     A2     A3     A4     A5     A6     A7
1061
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1062
    // A8     A9     AA     AB     AC     AD     AE     AF
1063
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1064
    // B0     B1     B2     B3     B4     B5     B6     B7
1065
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1066
    // B8     B9     BA     BB     BC     BD     BE     BF
1067
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1068
    // C0     C1     C2     C3     C4     C5     C6     C7
1069
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1070
    // C8     C9     CA     CB     CC     CD     CE     CF
1071
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1072
    // D0     D1     D2     D3     D4     D5     D6     D7
1073
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1074
    // D8     D9     DA     DB     DC     DD     DE     DF
1075
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1076
    // E0     E1     E2     E3     E4     E5     E6     E7
1077
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1078
    // E8     E9     EA     EB     EC     ED     EE     EF
1079
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1080
    // F0     F1     F2     F3     F4     F5     F6     F7
1081
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1082
    // F8     F9     FA     FB     FC     FD     FE     FF
1083
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1084
1085
0
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1086
0
  return !!(a[i >> 3] & (1 << (i & 7)));
1087
0
}
1088
1089
}  // namespace ada::character_sets
1090
1091
#endif  // ADA_CHARACTER_SETS_INL_H
1092
/* end file include/ada/character_sets-inl.h */
1093
/* begin file include/ada/checkers-inl.h */
1094
/**
1095
 * @file checkers-inl.h
1096
 * @brief Definitions for URL specific checkers used within Ada.
1097
 */
1098
#ifndef ADA_CHECKERS_INL_H
1099
#define ADA_CHECKERS_INL_H
1100
1101
#include <bit>
1102
#include <cstddef>
1103
#include <cstdint>
1104
#include <string_view>
1105
/* begin file include/ada/checkers.h */
1106
/**
1107
 * @file checkers.h
1108
 * @brief Declarations for URL specific checkers used within Ada.
1109
 */
1110
#ifndef ADA_CHECKERS_H
1111
#define ADA_CHECKERS_H
1112
1113
1114
#include <cstdint>
1115
#include <cstring>
1116
#include <string_view>
1117
1118
/**
1119
 * These functions are not part of our public API and may
1120
 * change at any time.
1121
 * @private
1122
 * @namespace ada::checkers
1123
 * @brief Includes the definitions for validation functions
1124
 */
1125
namespace ada::checkers {
1126
1127
/**
1128
 * @private
1129
 * Assuming that x is an ASCII letter, this function returns the lower case
1130
 * equivalent.
1131
 * @details More likely to be inlined by the compiler and constexpr.
1132
 */
1133
constexpr char to_lower(char x) noexcept;
1134
1135
/**
1136
 * @private
1137
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1138
 * but more likely to be inlined by the compiler.
1139
 *
1140
 * @attention std::isalpha is not constexpr generally.
1141
 */
1142
constexpr bool is_alpha(char x) noexcept;
1143
1144
/**
1145
 * @private
1146
 * Check whether x is an ASCII digit. More likely to be inlined than
1147
 * std::isdigit.
1148
 */
1149
constexpr bool is_digit(char x) noexcept;
1150
1151
/**
1152
 * @private
1153
 * @details A string starts with a Windows drive letter if all of the following
1154
 * are true:
1155
 *
1156
 *   - its length is greater than or equal to 2
1157
 *   - its first two code points are a Windows drive letter
1158
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1159
 * (?), or U+0023 (#).
1160
 *
1161
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1162
 */
1163
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1164
1165
/**
1166
 * @private
1167
 * @details A normalized Windows drive letter is a Windows drive letter of which
1168
 * the second code point is U+003A (:).
1169
 */
1170
inline constexpr bool is_normalized_windows_drive_letter(
1171
    std::string_view input) noexcept;
1172
1173
/**
1174
 * @private
1175
 * Returns true if an input is an ipv4 address. It is assumed that the string
1176
 * does not contain uppercase ASCII characters (the input should have been
1177
 * lowered cased before calling this function) and is not empty.
1178
 */
1179
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1180
1181
/**
1182
 * @private
1183
 * Cheap pre-check for the WHATWG "ends in a number" / IPv4-host cases.
1184
 * False means the host cannot be IPv4: the last label is empty, does not
1185
 * start with an ASCII digit, or contains a character other than a hex
1186
 * digit or x/X. True means the slower IPv4 parsers should run.
1187
 *
1188
 * Safe on mixed-case input (unlike is_ipv4, which expects a lowercased
1189
 * host).
1190
 */
1191
ada_really_inline constexpr bool last_label_may_be_a_number(
1192
    std::string_view view) noexcept;
1193
1194
/**
1195
 * @private
1196
 * Returns a bitset. If the first bit is set, then at least one character needs
1197
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1198
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1199
 * character.
1200
 */
1201
ada_really_inline constexpr uint8_t path_signature(
1202
    std::string_view input) noexcept;
1203
1204
/**
1205
 * @private
1206
 * Returns true if the length of the domain name and its labels are according to
1207
 * the specifications. The length of the domain must be 255 octets (253
1208
 * characters not including the last 2 which are the empty label reserved at the
1209
 * end). When the empty label is included (a dot at the end), the domain name
1210
 * can have 254 characters. The length of a label must be at least 1 and at most
1211
 * 63 characters.
1212
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1213
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1214
 */
1215
ada_really_inline constexpr bool verify_dns_length(
1216
    std::string_view input) noexcept;
1217
1218
/**
1219
 * @private
1220
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1221
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1222
 * to indicate failure (caller should fall back to general parser).
1223
 * This is optimized for the common case where the input is a well-formed
1224
 * decimal IPv4 address with exactly 4 octets.
1225
 */
1226
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1227
1228
/**
1229
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1230
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1231
 */
1232
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1233
1234
}  // namespace ada::checkers
1235
1236
#endif  // ADA_CHECKERS_H
1237
/* end file include/ada/checkers.h */
1238
1239
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1240
#include <immintrin.h>
1241
#endif
1242
1243
namespace ada::checkers {
1244
1245
0
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1246
1247
0
constexpr bool is_ipv4_number_char(char x) noexcept {
1248
0
  const unsigned char c = static_cast<unsigned char>(x);
1249
0
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1250
0
         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
1251
0
}
1252
1253
0
constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
1254
0
  if (view.empty()) {
1255
0
    return false;
1256
0
  }
1257
0
  const char* start = view.data();
1258
0
  const char* end = start + view.size();
1259
0
  if (end[-1] == '.') {
1260
0
    --end;
1261
0
    if (end == start) {
1262
0
      return false;
1263
0
    }
1264
0
  }
1265
0
  if (!is_ipv4_number_char(end[-1])) {
1266
0
    return false;
1267
0
  }
1268
0
  const char* label = end;
1269
0
  while (label != start && is_ipv4_number_char(label[-1])) {
1270
0
    --label;
1271
0
  }
1272
0
  if (label != start && label[-1] != '.') {
1273
0
    return false;
1274
0
  }
1275
0
  return label != end && is_digit(*label);
1276
0
}
1277
1278
0
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1279
1280
0
constexpr bool is_alpha(char x) noexcept {
1281
0
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1282
0
}
1283
1284
0
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1285
0
  return input.size() >= 2 &&
1286
0
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1287
0
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1288
0
                                  input[2] == '?' || input[2] == '#'));
1289
0
}
1290
1291
constexpr bool is_normalized_windows_drive_letter(
1292
0
    std::string_view input) noexcept {
1293
0
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1294
0
}
1295
1296
namespace detail {
1297
1298
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1299
ada_really_inline uint64_t
1300
0
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1301
0
  uint32_t ipv4 = 0;
1302
0
  for (int i = 0; i < 4; ++i) {
1303
0
    if (p == pend) [[unlikely]] {
1304
0
      return ipv4_fast_fail;
1305
0
    }
1306
0
    uint32_t val;
1307
0
    char c = *p;
1308
0
    if (c >= '0' && c <= '9') [[likely]] {
1309
0
      val = static_cast<uint32_t>(c - '0');
1310
0
      ++p;
1311
0
    } else {
1312
0
      return ipv4_fast_fail;
1313
0
    }
1314
0
    if (p < pend) {
1315
0
      c = *p;
1316
0
      if (c >= '0' && c <= '9') {
1317
0
        if (val == 0) [[unlikely]] {
1318
0
          return ipv4_fast_fail;
1319
0
        }
1320
0
        val = val * 10u + static_cast<uint32_t>(c - '0');
1321
0
        ++p;
1322
0
        if (p < pend) {
1323
0
          c = *p;
1324
0
          if (c >= '0' && c <= '9') {
1325
0
            val = val * 10u + static_cast<uint32_t>(c - '0');
1326
0
            ++p;
1327
0
            if (val > 255u) [[unlikely]] {
1328
0
              return ipv4_fast_fail;
1329
0
            }
1330
0
          }
1331
0
        }
1332
0
      }
1333
0
    }
1334
0
    ipv4 = (ipv4 << 8) | val;
1335
0
    if (i < 3) {
1336
0
      if (p == pend || *p != '.') [[unlikely]] {
1337
0
        return ipv4_fast_fail;
1338
0
      }
1339
0
      ++p;
1340
0
    }
1341
0
  }
1342
0
  if (p != pend) {
1343
0
    if (p == pend - 1 && *p == '.') {
1344
0
      return ipv4;
1345
0
    }
1346
0
    return ipv4_fast_fail;
1347
0
  }
1348
0
  return ipv4;
1349
0
}
1350
1351
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1352
// Table-free AVX-512VL IPv4 parse (simdip parse_ipv4_avx512vl_notab5).
1353
// Needs VBMI2 for vpcompressb; ADA_AVX512 stays BW+VL (IPv6 does not use
1354
// VBMI2). Masked load of exactly `len` bytes (no over-read). Digit
1355
// placement is computed from compressed delimiter positions; octet > 255
1356
// is a dword compare on the zero-padded reversed digit group, in parallel
1357
// with the convert. Unusual-but-valid forms (octal, hex, leading zeros,
1358
// fewer than four parts) return ipv4_fast_fail so the general parser runs.
1359
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1360
                                                 size_t len) noexcept {
1361
  // One trailing dot is WHATWG-legal ("1.2.3.4."); the SIMD kernel is
1362
  // strict four-group dotted-decimal.
1363
  if (data[len - 1] == '.') {
1364
    --len;
1365
  }
1366
  if (len > 15) [[unlikely]] {
1367
    return ipv4_fast_fail;
1368
  }
1369
1370
#if defined(__BMI2__)
1371
  const uint32_t len_mask = _bzhi_u32(0xFFFFFFFFu, static_cast<unsigned>(len));
1372
#else
1373
  const uint32_t len_mask = (1u << static_cast<unsigned>(len)) - 1u;
1374
#endif
1375
  const __mmask16 len_k = static_cast<__mmask16>(len_mask);
1376
  const __m128i dot = _mm_set1_epi8('.');
1377
  const __m128i v =
1378
      _mm_mask_loadu_epi8(dot, len_k, reinterpret_cast<const void*>(data));
1379
1380
  const __mmask16 delim = _mm_cmpeq_epi8_mask(v, dot);
1381
  const uint32_t dots = static_cast<uint32_t>(delim) & len_mask;
1382
  const uint32_t keep = len_mask & ~dots;
1383
1384
  const __m128i zero = _mm_set1_epi8('0');
1385
  const __m128i digits = _mm_sub_epi8(v, zero);
1386
  const __mmask16 is_digit = _mm_cmple_epu8_mask(digits, _mm_set1_epi8(9));
1387
  // Junk in a digit slot: inside [0, len) yet neither a digit nor a dot.
1388
  const __mmask16 hole = _kandn_mask16(_kor_mask16(delim, is_digit), len_k);
1389
  const __m128i v0 = _mm_maskz_mov_epi8(is_digit, digits);
1390
1391
  const __m128i iota =
1392
      _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
1393
  const __m128i c = _mm_maskz_compress_epi8(delim, iota);
1394
  const __m128i k_rep =
1395
      _mm_setr_epi8(0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3);
1396
  const __m128i qi = _mm_shuffle_epi8(c, k_rep);
1397
  const __m128i prev =
1398
      _mm_shuffle_epi8(_mm_alignr_epi8(c, _mm_set1_epi8(-1), 15), k_rep);
1399
  // Reversed group order: lane 4i+j fetches q_i-(j+1), so the dword is
1400
  // ones | tens<<8 | hundreds<<16, monotone in the decimal value.
1401
  const __m128i offr = _mm_setr_epi8(-1, -2, -3, -4, -1, -2, -3, -4, -1, -2, -3,
1402
                                     -4, -1, -2, -3, -4);
1403
  const __m128i idx = _mm_max_epi8(_mm_add_epi8(qi, offr), prev);
1404
  const __m128i padded = _mm_shuffle_epi8(v0, idx);
1405
1406
  const __m128i lim =
1407
      _mm_setr_epi8(5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0);
1408
  const __mmask8 over = _mm_cmpgt_epu32_mask(padded, lim);
1409
  const __m128i wtsr =
1410
      _mm_setr_epi8(1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0);
1411
#if defined(__AVX512VNNI__)
1412
  const __m128i res = _mm_dpbusd_epi32(_mm_setzero_si128(), padded, wtsr);
1413
#else
1414
  const __m128i res =
1415
      _mm_madd_epi16(_mm_maddubs_epi16(padded, wtsr), _mm_set1_epi16(1));
1416
#endif
1417
1418
  const __m128i gmin =
1419
      _mm_setr_epi8(1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1420
  const __m128i gmax =
1421
      _mm_setr_epi8(2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
1422
  const __m128i gap = _mm_sub_epi8(c, _mm_slli_si128(c, 1));
1423
  const __mmask16 bad_gap = _mm_cmpgt_epu8_mask(_mm_sub_epi8(gap, gmin), gmax);
1424
1425
  const uint32_t zero_bits =
1426
      static_cast<uint32_t>(_mm_cmpeq_epi8_mask(v, zero));
1427
  const uint32_t start_bits = (dots << 1) | 1u;
1428
1429
  const __mmask16 kerr =
1430
      _kor_mask16(_kor_mask16(hole, static_cast<__mmask16>(over)), bad_gap);
1431
  const uint32_t gerr = static_cast<uint32_t>(_mm_popcnt_u32(dots) ^ 3u) |
1432
                        (zero_bits & start_bits & (keep >> 1));
1433
1434
  if ((gerr | static_cast<uint32_t>(kerr)) == 0) [[likely]] {
1435
    const uint32_t packed =
1436
        static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_cvtepi32_epi8(res)));
1437
#if defined(__GNUC__) || defined(__clang__)
1438
    return static_cast<uint64_t>(__builtin_bswap32(packed));
1439
#else
1440
    return static_cast<uint64_t>(_byteswap_ulong(packed));
1441
#endif
1442
  }
1443
  return ipv4_fast_fail;
1444
}
1445
#endif  // ADA_AVX512 && __AVX512VBMI2__
1446
1447
}  // namespace detail
1448
1449
/**
1450
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1451
 * Accepts an optional single trailing dot.
1452
 *
1453
 * On AVX-512BW+VL+VBMI2 targets, uses a table-free SIMD parse (masked
1454
 * load, no source over-read) based on parse_ipv4_avx512vl_notab5.
1455
 * Otherwise uses an unrolled scalar path (typically faster than SSE2/NEON
1456
 * pre-validation for these 7-16 byte hosts).
1457
 */
1458
ada_really_inline uint64_t
1459
0
try_parse_ipv4_fast(std::string_view input) noexcept {
1460
0
  const size_t len = input.size();
1461
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1462
0
  if (len < 7 || len > 16) [[unlikely]] {
1463
0
    return ipv4_fast_fail;
1464
0
  }
1465
0
  const char* data = input.data();
1466
1467
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1468
  return detail::try_parse_ipv4_avx512(data, len);
1469
#else
1470
0
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1471
0
#endif
1472
0
}
1473
1474
}  // namespace ada::checkers
1475
1476
#endif  // ADA_CHECKERS_INL_H
1477
/* end file include/ada/checkers-inl.h */
1478
/* begin file include/ada/log.h */
1479
/**
1480
 * @file log.h
1481
 * @brief Includes the definitions for logging.
1482
 * @private Excluded from docs through the doxygen file.
1483
 */
1484
#ifndef ADA_LOG_H
1485
#define ADA_LOG_H
1486
1487
// To enable logging, set ADA_LOGGING to 1:
1488
#ifndef ADA_LOGGING
1489
#define ADA_LOGGING 0
1490
#endif
1491
1492
#if ADA_LOGGING
1493
#include <iostream>
1494
#endif  // ADA_LOGGING
1495
1496
namespace ada {
1497
1498
/**
1499
 * Log a message. If you want to have no overhead when logging is disabled, use
1500
 * the ada_log macro.
1501
 * @private
1502
 */
1503
template <typename... Args>
1504
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1505
#if ADA_LOGGING
1506
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1507
#endif  // ADA_LOGGING
1508
}
1509
}  // namespace ada
1510
1511
#if ADA_LOGGING
1512
#ifndef ada_log
1513
#define ada_log(...)       \
1514
  do {                     \
1515
    ada::log(__VA_ARGS__); \
1516
  } while (0)
1517
#endif  // ada_log
1518
#else
1519
#define ada_log(...)
1520
#endif  // ADA_LOGGING
1521
1522
#endif  // ADA_LOG_H
1523
/* end file include/ada/log.h */
1524
/* begin file include/ada/encoding_type.h */
1525
/**
1526
 * @file encoding_type.h
1527
 * @brief Character encoding type definitions.
1528
 *
1529
 * Defines the encoding types supported for URL processing.
1530
 *
1531
 * @see https://encoding.spec.whatwg.org/
1532
 */
1533
#ifndef ADA_ENCODING_TYPE_H
1534
#define ADA_ENCODING_TYPE_H
1535
1536
#include <string>
1537
#include <string_view>
1538
1539
namespace ada {
1540
1541
/**
1542
 * @brief Character encoding types for URL processing.
1543
 *
1544
 * Specifies the character encoding used for percent-decoding and other
1545
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1546
 *
1547
 * @see https://encoding.spec.whatwg.org/#encodings
1548
 */
1549
enum class encoding_type {
1550
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1551
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1552
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1553
};
1554
1555
/**
1556
 * Converts an encoding_type to its string representation.
1557
 * @param type The encoding type to convert.
1558
 * @return A string view of the encoding name.
1559
 */
1560
ada_warn_unused std::string_view to_string(encoding_type type);
1561
1562
}  // namespace ada
1563
1564
#endif  // ADA_ENCODING_TYPE_H
1565
/* end file include/ada/encoding_type.h */
1566
/* begin file include/ada/helpers.h */
1567
/**
1568
 * @file helpers.h
1569
 * @brief Definitions for helper functions used within Ada.
1570
 */
1571
#ifndef ADA_HELPERS_H
1572
#define ADA_HELPERS_H
1573
1574
/* begin file include/ada/url_base.h */
1575
/**
1576
 * @file url_base.h
1577
 * @brief Base class and common definitions for URL types.
1578
 *
1579
 * This file defines the `url_base` abstract base class from which both
1580
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1581
 * enumerations like `url_host_type`.
1582
 */
1583
#ifndef ADA_URL_BASE_H
1584
#define ADA_URL_BASE_H
1585
1586
/* begin file include/ada/scheme.h */
1587
/**
1588
 * @file scheme.h
1589
 * @brief URL scheme type definitions and utilities.
1590
 *
1591
 * This header defines the URL scheme types (http, https, etc.) and provides
1592
 * functions to identify special schemes and their default ports according
1593
 * to the WHATWG URL Standard.
1594
 *
1595
 * @see https://url.spec.whatwg.org/#special-scheme
1596
 */
1597
#ifndef ADA_SCHEME_H
1598
#define ADA_SCHEME_H
1599
1600
1601
#include <cstdint>
1602
#include <string>
1603
#include <string_view>
1604
1605
/**
1606
 * @namespace ada::scheme
1607
 * @brief URL scheme utilities and constants.
1608
 *
1609
 * Provides functions for working with URL schemes, including identification
1610
 * of special schemes and retrieval of default port numbers.
1611
 */
1612
namespace ada::scheme {
1613
1614
/**
1615
 * @brief Enumeration of URL scheme types.
1616
 *
1617
 * Special schemes have specific parsing rules and default ports.
1618
 * Using an enum allows efficient scheme comparisons without string operations.
1619
 *
1620
 * Default ports:
1621
 * - HTTP: 80
1622
 * - HTTPS: 443
1623
 * - WS: 80
1624
 * - WSS: 443
1625
 * - FTP: 21
1626
 * - FILE: (none)
1627
 */
1628
enum type : uint8_t {
1629
  HTTP = 0,        /**< http:// scheme (port 80) */
1630
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1631
  HTTPS = 2,       /**< https:// scheme (port 443) */
1632
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1633
  FTP = 4,         /**< ftp:// scheme (port 21) */
1634
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1635
  FILE = 6         /**< file:// scheme (no default port) */
1636
};
1637
1638
/**
1639
 * Checks if a scheme string is a special scheme.
1640
 * @param scheme The scheme string to check (e.g., "http", "https").
1641
 * @return `true` if the scheme is special, `false` otherwise.
1642
 * @see https://url.spec.whatwg.org/#special-scheme
1643
 */
1644
ada_really_inline constexpr bool is_special(std::string_view scheme);
1645
1646
/**
1647
 * Returns the default port for a special scheme string.
1648
 * @param scheme The scheme string (e.g., "http", "https").
1649
 * @return The default port number, or 0 if not a special scheme.
1650
 * @see https://url.spec.whatwg.org/#special-scheme
1651
 */
1652
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1653
1654
/**
1655
 * Returns the default port for a scheme type.
1656
 * @param type The scheme type enum value.
1657
 * @return The default port number, or 0 if not applicable.
1658
 * @see https://url.spec.whatwg.org/#special-scheme
1659
 */
1660
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1661
1662
/**
1663
 * Converts a scheme string to its type enum.
1664
 * @param scheme The scheme string to convert.
1665
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1666
 */
1667
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1668
1669
}  // namespace ada::scheme
1670
1671
#endif  // ADA_SCHEME_H
1672
/* end file include/ada/scheme.h */
1673
1674
#include <cstddef>
1675
#include <cstdint>
1676
#include <string>
1677
#include <string_view>
1678
1679
namespace ada {
1680
1681
/**
1682
 * @brief Enum representing the type of host in a URL.
1683
 *
1684
 * Used to distinguish between regular domain names, IPv4 addresses,
1685
 * and IPv6 addresses for proper parsing and serialization.
1686
 */
1687
enum url_host_type : uint8_t {
1688
  /** Regular domain name (e.g., "www.example.com") */
1689
  DEFAULT = 0,
1690
  /** IPv4 address (e.g., "127.0.0.1") */
1691
  IPV4 = 1,
1692
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1693
  IPV6 = 2,
1694
};
1695
1696
/**
1697
 * @brief Abstract base class for URL representations.
1698
 *
1699
 * The `url_base` class provides the common interface and state shared by
1700
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1701
 * like validity status and scheme type, but delegates component storage and
1702
 * access to derived classes.
1703
 *
1704
 * @note This is an abstract class and cannot be instantiated directly.
1705
 *       Use `ada::url` or `ada::url_aggregator` instead.
1706
 *
1707
 * @see url
1708
 * @see url_aggregator
1709
 */
1710
struct url_base {
1711
0
  virtual ~url_base() = default;
1712
1713
  /**
1714
   * Indicates whether the URL was successfully parsed.
1715
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1716
   */
1717
  bool is_valid{true};
1718
1719
  /**
1720
   * Indicates whether the URL has an opaque path (non-hierarchical).
1721
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1722
   */
1723
  bool has_opaque_path{false};
1724
1725
  /**
1726
   * The type of the URL's host (domain, IPv4, or IPv6).
1727
   */
1728
  url_host_type host_type = url_host_type::DEFAULT;
1729
1730
  /**
1731
   * @private
1732
   * Internal representation of the URL's scheme type.
1733
   */
1734
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1735
1736
  /**
1737
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1738
   * Special schemes have specific parsing rules and default ports.
1739
   * @return `true` if the scheme is special, `false` otherwise.
1740
   */
1741
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1742
1743
  /**
1744
   * Returns the URL's origin (scheme + host + port for special URLs).
1745
   * @return A newly allocated string containing the serialized origin.
1746
   * @see https://url.spec.whatwg.org/#concept-url-origin
1747
   */
1748
  [[nodiscard]] virtual std::string get_origin() const = 0;
1749
1750
  /**
1751
   * Validates whether the hostname is a valid domain according to RFC 1034.
1752
   * Checks that the domain and its labels have valid lengths.
1753
   * @return `true` if the domain is valid, `false` otherwise.
1754
   */
1755
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1756
1757
  /**
1758
   * @private
1759
   * Returns the default port for special schemes (e.g., 443 for https).
1760
   * Returns 0 for file:// URLs or non-special schemes.
1761
   */
1762
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1763
1764
  /**
1765
   * @private
1766
   * Returns the default port for the URL's scheme, or 0 if none.
1767
   */
1768
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1769
1770
  /**
1771
   * @private
1772
   * Parses a port number from the input string.
1773
   * @param view The string containing the port to parse.
1774
   * @param check_trailing_content Whether to validate no trailing characters.
1775
   * @return Number of bytes consumed on success, 0 on failure.
1776
   */
1777
  virtual size_t parse_port(std::string_view view,
1778
                            bool check_trailing_content) = 0;
1779
1780
  /** @private */
1781
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1782
0
    return this->parse_port(view, false);
1783
0
  }
1784
1785
  /**
1786
   * Returns a JSON string representation of this URL for debugging.
1787
   * @return A JSON-formatted string with URL information.
1788
   */
1789
  [[nodiscard]] virtual std::string to_string() const = 0;
1790
1791
  /** @private */
1792
  virtual inline void clear_pathname() = 0;
1793
1794
  /** @private */
1795
  virtual inline void clear_search() = 0;
1796
1797
  /** @private */
1798
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1799
1800
  /** @private */
1801
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1802
1803
};  // url_base
1804
1805
}  // namespace ada
1806
1807
#endif
1808
/* end file include/ada/url_base.h */
1809
1810
#include <cstddef>
1811
#include <cstdint>
1812
#include <optional>
1813
#include <string>
1814
#include <string_view>
1815
#include <utility>
1816
1817
#if ADA_DEVELOPMENT_CHECKS
1818
#include <cstdlib>
1819
#include <iostream>
1820
#endif  // ADA_DEVELOPMENT_CHECKS
1821
1822
/**
1823
 * These functions are not part of our public API and may
1824
 * change at any time.
1825
 *
1826
 * @private
1827
 * @namespace ada::helpers
1828
 * @brief Includes the definitions for helper functions
1829
 */
1830
namespace ada::helpers {
1831
1832
/**
1833
 * @private
1834
 */
1835
template <typename out_iter>
1836
void encode_json(std::string_view view, out_iter out);
1837
1838
/**
1839
 * @private
1840
 * This function is used to prune a fragment from a url, and returning the
1841
 * removed string if input has fragment.
1842
 *
1843
 * @details prune_hash seeks the first '#' and returns everything after it
1844
 * as a string_view, and modifies (in place) the input so that it points at
1845
 * everything before the '#'. If no '#' is found, the input is left unchanged
1846
 * and std::nullopt is returned.
1847
 *
1848
 * @attention The function is non-allocating and it does not throw.
1849
 * @returns Note that the returned string_view might be empty!
1850
 */
1851
ada_really_inline std::optional<std::string_view> prune_hash(
1852
    std::string_view& input) noexcept;
1853
1854
/**
1855
 * @private
1856
 * Defined by the URL specification, shorten a URLs paths.
1857
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1858
 * @returns Returns true if path is shortened.
1859
 */
1860
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1861
1862
/**
1863
 * @private
1864
 * Defined by the URL specification, shorten a URLs paths.
1865
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1866
 * @returns Returns true if path is shortened.
1867
 */
1868
ada_really_inline bool shorten_path(std::string_view& path,
1869
                                    ada::scheme::type type);
1870
1871
/**
1872
 * @private
1873
 *
1874
 * Parse the path from the provided input and append to the existing
1875
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1876
 * is the user's responsibility to check.
1877
 *
1878
 * The input is expected to be UTF-8.
1879
 *
1880
 * @see https://url.spec.whatwg.org/
1881
 */
1882
ada_really_inline void parse_prepared_path(std::string_view input,
1883
                                           ada::scheme::type type,
1884
                                           std::string& path);
1885
1886
/**
1887
 * @private
1888
 * Remove and mutate all ASCII tab or newline characters from an input.
1889
 */
1890
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1891
1892
/**
1893
 * @private
1894
 * Return the substring from input going from index pos to the end.
1895
 */
1896
ada_really_inline constexpr std::string_view substring(std::string_view input,
1897
                                                       size_t pos);
1898
1899
/**
1900
 * @private
1901
 * Returns true if the string_view points within the string.
1902
 */
1903
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1904
1905
/**
1906
 * @private
1907
 * Return the substring from input going from index pos1 to the pos2 (non
1908
 * included). The length of the substring is pos2 - pos1.
1909
 */
1910
ada_really_inline constexpr std::string_view substring(std::string_view input,
1911
                                                       size_t pos1,
1912
0
                                                       size_t pos2) {
1913
#if ADA_DEVELOPMENT_CHECKS
1914
  if (pos2 < pos1) {
1915
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1916
              << std::endl;
1917
    abort();
1918
  }
1919
#endif
1920
0
  return input.substr(pos1, pos2 - pos1);
1921
0
}
1922
1923
/**
1924
 * @private
1925
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1926
 * input.size(). This function cannot throw.
1927
 */
1928
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1929
1930
/**
1931
 * @private
1932
 * Returns a host's delimiter location depending on the state of the instance,
1933
 * and whether a colon was found outside brackets. Used by the host parser.
1934
 */
1935
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1936
    bool is_special, std::string_view& view) noexcept;
1937
1938
/**
1939
 * @private
1940
 * Removes leading and trailing C0 control and whitespace characters from
1941
 * string.
1942
 */
1943
void trim_c0_whitespace(std::string_view& input) noexcept;
1944
1945
/**
1946
 * @private
1947
 * @see
1948
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1949
 */
1950
template <class url_type>
1951
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1952
1953
/**
1954
 * @private
1955
 * Finds the delimiter of a view in authority state.
1956
 */
1957
ada_really_inline size_t
1958
find_authority_delimiter_special(std::string_view view) noexcept;
1959
1960
/**
1961
 * @private
1962
 * Finds the delimiter of a view in authority state.
1963
 */
1964
ada_really_inline size_t
1965
find_authority_delimiter(std::string_view view) noexcept;
1966
1967
/**
1968
 * @private
1969
 */
1970
template <typename T, typename... Args>
1971
0
inline void inner_concat(std::string& buffer, T t) {
1972
0
  buffer.append(t);
1973
0
}
Unexecuted instantiation: 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> >)
Unexecuted instantiation: void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Unexecuted instantiation: 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> >)
1974
1975
/**
1976
 * @private
1977
 */
1978
template <typename T, typename... Args>
1979
0
inline void inner_concat(std::string& buffer, T t, Args... args) {
1980
0
  buffer.append(t);
1981
0
  return inner_concat(buffer, args...);
1982
0
}
Unexecuted instantiation: 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> >)
Unexecuted instantiation: 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> >)
Unexecuted instantiation: 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*)
Unexecuted instantiation: 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*)
Unexecuted instantiation: 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> >)
Unexecuted instantiation: 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> >)
1983
1984
/**
1985
 * @private
1986
 * Concatenate the arguments and return a string.
1987
 * @returns a string
1988
 */
1989
template <typename... Args>
1990
0
std::string concat(Args... args) {
1991
0
  std::string answer;
1992
0
  inner_concat(answer, args...);
1993
0
  return answer;
1994
0
}
Unexecuted instantiation: 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> >)
Unexecuted instantiation: 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> >)
Unexecuted instantiation: 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*)
Unexecuted instantiation: 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*)
Unexecuted instantiation: 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> >)
Unexecuted instantiation: 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> >)
1995
1996
/**
1997
 * @private
1998
 * @return Number of leading zeroes.
1999
 */
2000
0
inline int leading_zeroes(uint32_t input_num) noexcept {
2001
#if ADA_REGULAR_VISUAL_STUDIO
2002
  unsigned long leading_zero(0);
2003
  unsigned long in(input_num);
2004
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
2005
#else
2006
0
  return __builtin_clz(input_num);
2007
0
#endif  // ADA_REGULAR_VISUAL_STUDIO
2008
0
}
2009
2010
/**
2011
 * @private
2012
 * Counts the number of decimal digits necessary to represent x.
2013
 * faster than std::to_string(x).size().
2014
 * @return digit count
2015
 */
2016
0
inline int fast_digit_count(uint32_t x) noexcept {
2017
0
  auto int_log2 = [](uint32_t z) -> int {
2018
0
    return 31 - ada::helpers::leading_zeroes(z | 1);
2019
0
  };
2020
0
  // Compiles to very few instructions. Note that the
2021
0
  // table is static and thus effectively a constant.
2022
0
  // We leave it inside the function because it is meaningless
2023
0
  // outside of it (this comes at no performance cost).
2024
0
  const static uint64_t table[] = {
2025
0
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
2026
0
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
2027
0
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
2028
0
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
2029
0
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
2030
0
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
2031
0
      42949672960, 42949672960};
2032
0
  return int((x + table[int_log2(x)]) >> 32);
2033
0
}
2034
}  // namespace ada::helpers
2035
2036
#endif  // ADA_HELPERS_H
2037
/* end file include/ada/helpers.h */
2038
/* begin file include/ada/parser.h */
2039
/**
2040
 * @file parser.h
2041
 * @brief Low-level URL parsing functions.
2042
 *
2043
 * This header provides the internal URL parsing implementation. Most users
2044
 * should use `ada::parse()` from implementation.h instead of these functions
2045
 * directly.
2046
 *
2047
 * @see implementation.h for the recommended public API
2048
 */
2049
#ifndef ADA_PARSER_H
2050
#define ADA_PARSER_H
2051
2052
#include <cstddef>
2053
#include <cstdint>
2054
#include <string_view>
2055
#include <variant>
2056
2057
/* begin file include/ada/expected.h */
2058
/**
2059
 * @file expected.h
2060
 * @brief Definitions for std::expected
2061
 * @private Excluded from docs through the doxygen file.
2062
 */
2063
///
2064
// expected - An implementation of std::expected with extensions
2065
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
2066
//
2067
// Documentation available at http://tl.tartanllama.xyz/
2068
//
2069
// To the extent possible under law, the author(s) have dedicated all
2070
// copyright and related and neighboring rights to this software to the
2071
// public domain worldwide. This software is distributed without any warranty.
2072
//
2073
// You should have received a copy of the CC0 Public Domain Dedication
2074
// along with this software. If not, see
2075
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2076
///
2077
2078
#ifndef TL_EXPECTED_HPP
2079
#define TL_EXPECTED_HPP
2080
2081
#define TL_EXPECTED_VERSION_MAJOR 1
2082
#define TL_EXPECTED_VERSION_MINOR 1
2083
#define TL_EXPECTED_VERSION_PATCH 0
2084
2085
#include <cstddef>
2086
#include <exception>
2087
#include <functional>
2088
#include <initializer_list>
2089
#include <type_traits>
2090
#include <utility>
2091
2092
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2093
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2094
#endif
2095
2096
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2097
#define TL_EXPECTED_MSVC2015
2098
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2099
#else
2100
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2101
#endif
2102
2103
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2104
     !defined(__clang__))
2105
#define TL_EXPECTED_GCC49
2106
#endif
2107
2108
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2109
     !defined(__clang__))
2110
#define TL_EXPECTED_GCC54
2111
#endif
2112
2113
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2114
     !defined(__clang__))
2115
#define TL_EXPECTED_GCC55
2116
#endif
2117
2118
#if !defined(TL_ASSERT)
2119
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2120
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2121
#include <cassert>
2122
0
#define TL_ASSERT(x) assert(x)
2123
#else
2124
#define TL_ASSERT(x)
2125
#endif
2126
#endif
2127
2128
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2129
     !defined(__clang__))
2130
// GCC < 5 doesn't support overloading on const&& for member functions
2131
2132
#define TL_EXPECTED_NO_CONSTRR
2133
// GCC < 5 doesn't support some standard C++11 type traits
2134
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2135
  std::has_trivial_copy_constructor<T>
2136
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2137
  std::has_trivial_copy_assign<T>
2138
2139
// This one will be different for GCC 5.7 if it's ever supported
2140
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2141
  std::is_trivially_destructible<T>
2142
2143
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2144
// std::vector for non-copyable types
2145
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2146
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2147
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2148
namespace tl {
2149
namespace detail {
2150
template <class T>
2151
struct is_trivially_copy_constructible
2152
    : std::is_trivially_copy_constructible<T> {};
2153
#ifdef _GLIBCXX_VECTOR
2154
template <class T, class A>
2155
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2156
#endif
2157
}  // namespace detail
2158
}  // namespace tl
2159
#endif
2160
2161
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2162
  tl::detail::is_trivially_copy_constructible<T>
2163
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2164
  std::is_trivially_copy_assignable<T>
2165
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2166
  std::is_trivially_destructible<T>
2167
#else
2168
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2169
  std::is_trivially_copy_constructible<T>
2170
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2171
  std::is_trivially_copy_assignable<T>
2172
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2173
  std::is_trivially_destructible<T>
2174
#endif
2175
2176
#if __cplusplus > 201103L
2177
#define TL_EXPECTED_CXX14
2178
#endif
2179
2180
#ifdef TL_EXPECTED_GCC49
2181
#define TL_EXPECTED_GCC49_CONSTEXPR
2182
#else
2183
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2184
#endif
2185
2186
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2187
     defined(TL_EXPECTED_GCC49))
2188
#define TL_EXPECTED_11_CONSTEXPR
2189
#else
2190
#define TL_EXPECTED_11_CONSTEXPR constexpr
2191
#endif
2192
2193
namespace tl {
2194
template <class T, class E>
2195
class expected;
2196
2197
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2198
#define TL_MONOSTATE_INPLACE_MUTEX
2199
class monostate {};
2200
2201
struct in_place_t {
2202
  explicit in_place_t() = default;
2203
};
2204
static constexpr in_place_t in_place{};
2205
#endif
2206
2207
template <class E>
2208
class unexpected {
2209
 public:
2210
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2211
2212
  unexpected() = delete;
2213
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2214
2215
0
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2216
2217
  template <class... Args, typename std::enable_if<std::is_constructible<
2218
                               E, Args&&...>::value>::type* = nullptr>
2219
  constexpr explicit unexpected(Args&&... args)
2220
0
      : m_val(std::forward<Args>(args)...) {}
2221
  template <
2222
      class U, class... Args,
2223
      typename std::enable_if<std::is_constructible<
2224
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2225
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2226
      : m_val(l, std::forward<Args>(args)...) {}
2227
2228
  constexpr const E& value() const& { return m_val; }
2229
0
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2230
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2231
  constexpr const E&& value() const&& { return std::move(m_val); }
2232
2233
 private:
2234
  E m_val;
2235
};
2236
2237
#ifdef __cpp_deduction_guides
2238
template <class E>
2239
unexpected(E) -> unexpected<E>;
2240
#endif
2241
2242
template <class E>
2243
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2244
  return lhs.value() == rhs.value();
2245
}
2246
template <class E>
2247
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2248
  return lhs.value() != rhs.value();
2249
}
2250
template <class E>
2251
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2252
  return lhs.value() < rhs.value();
2253
}
2254
template <class E>
2255
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2256
  return lhs.value() <= rhs.value();
2257
}
2258
template <class E>
2259
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2260
  return lhs.value() > rhs.value();
2261
}
2262
template <class E>
2263
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2264
  return lhs.value() >= rhs.value();
2265
}
2266
2267
template <class E>
2268
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2269
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2270
}
2271
2272
struct unexpect_t {
2273
  unexpect_t() = default;
2274
};
2275
static constexpr unexpect_t unexpect{};
2276
2277
namespace detail {
2278
template <typename E>
2279
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2280
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2281
0
  throw std::forward<E>(e);
2282
#else
2283
  (void)e;
2284
#ifdef _MSC_VER
2285
  __assume(0);
2286
#else
2287
  __builtin_unreachable();
2288
#endif
2289
#endif
2290
0
}
2291
2292
#ifndef TL_TRAITS_MUTEX
2293
#define TL_TRAITS_MUTEX
2294
// C++14-style aliases for brevity
2295
template <class T>
2296
using remove_const_t = typename std::remove_const<T>::type;
2297
template <class T>
2298
using remove_reference_t = typename std::remove_reference<T>::type;
2299
template <class T>
2300
using decay_t = typename std::decay<T>::type;
2301
template <bool E, class T = void>
2302
using enable_if_t = typename std::enable_if<E, T>::type;
2303
template <bool B, class T, class F>
2304
using conditional_t = typename std::conditional<B, T, F>::type;
2305
2306
// std::conjunction from C++17
2307
template <class...>
2308
struct conjunction : std::true_type {};
2309
template <class B>
2310
struct conjunction<B> : B {};
2311
template <class B, class... Bs>
2312
struct conjunction<B, Bs...>
2313
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2314
2315
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2316
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2317
#endif
2318
2319
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2320
// which results in a hard-error when using it in a noexcept expression
2321
// in some cases. This is a check to workaround the common failing case.
2322
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2323
template <class T>
2324
struct is_pointer_to_non_const_member_func : std::false_type {};
2325
template <class T, class Ret, class... Args>
2326
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2327
    : std::true_type {};
2328
template <class T, class Ret, class... Args>
2329
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2330
    : std::true_type {};
2331
template <class T, class Ret, class... Args>
2332
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2333
    : std::true_type {};
2334
template <class T, class Ret, class... Args>
2335
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2336
    : std::true_type {};
2337
template <class T, class Ret, class... Args>
2338
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2339
    : std::true_type {};
2340
template <class T, class Ret, class... Args>
2341
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2342
    : std::true_type {};
2343
2344
template <class T>
2345
struct is_const_or_const_ref : std::false_type {};
2346
template <class T>
2347
struct is_const_or_const_ref<T const&> : std::true_type {};
2348
template <class T>
2349
struct is_const_or_const_ref<T const> : std::true_type {};
2350
#endif
2351
2352
// std::invoke from C++17
2353
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2354
template <
2355
    typename Fn, typename... Args,
2356
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2357
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2358
                             is_const_or_const_ref<Args...>::value)>,
2359
#endif
2360
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2361
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2362
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2363
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2364
  return std::mem_fn(f)(std::forward<Args>(args)...);
2365
}
2366
2367
template <typename Fn, typename... Args,
2368
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2369
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2370
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2371
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2372
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2373
}
2374
2375
// std::invoke_result from C++17
2376
template <class F, class, class... Us>
2377
struct invoke_result_impl;
2378
2379
template <class F, class... Us>
2380
struct invoke_result_impl<
2381
    F,
2382
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2383
    Us...> {
2384
  using type =
2385
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2386
};
2387
2388
template <class F, class... Us>
2389
using invoke_result = invoke_result_impl<F, void, Us...>;
2390
2391
template <class F, class... Us>
2392
using invoke_result_t = typename invoke_result<F, Us...>::type;
2393
2394
#if defined(_MSC_VER) && _MSC_VER <= 1900
2395
// TODO make a version which works with MSVC 2015
2396
template <class T, class U = T>
2397
struct is_swappable : std::true_type {};
2398
2399
template <class T, class U = T>
2400
struct is_nothrow_swappable : std::true_type {};
2401
#else
2402
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2403
namespace swap_adl_tests {
2404
// if swap ADL finds this then it would call std::swap otherwise (same
2405
// signature)
2406
struct tag {};
2407
2408
template <class T>
2409
tag swap(T&, T&);
2410
template <class T, std::size_t N>
2411
tag swap(T (&a)[N], T (&b)[N]);
2412
2413
// helper functions to test if an unqualified swap is possible, and if it
2414
// becomes std::swap
2415
template <class, class>
2416
std::false_type can_swap(...) noexcept(false);
2417
template <class T, class U,
2418
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2419
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2420
                                                    std::declval<U&>())));
2421
2422
template <class, class>
2423
std::false_type uses_std(...);
2424
template <class T, class U>
2425
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2426
uses_std(int);
2427
2428
template <class T>
2429
struct is_std_swap_noexcept
2430
    : std::integral_constant<bool,
2431
                             std::is_nothrow_move_constructible<T>::value &&
2432
                                 std::is_nothrow_move_assignable<T>::value> {};
2433
2434
template <class T, std::size_t N>
2435
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2436
2437
template <class T, class U>
2438
struct is_adl_swap_noexcept
2439
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2440
}  // namespace swap_adl_tests
2441
2442
template <class T, class U = T>
2443
struct is_swappable
2444
    : std::integral_constant<
2445
          bool,
2446
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2447
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2448
               (std::is_move_assignable<T>::value &&
2449
                std::is_move_constructible<T>::value))> {};
2450
2451
template <class T, std::size_t N>
2452
struct is_swappable<T[N], T[N]>
2453
    : std::integral_constant<
2454
          bool,
2455
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2456
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2457
                   0))::value ||
2458
               is_swappable<T, T>::value)> {};
2459
2460
template <class T, class U = T>
2461
struct is_nothrow_swappable
2462
    : std::integral_constant<
2463
          bool,
2464
          is_swappable<T, U>::value &&
2465
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2466
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2467
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2468
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2469
#endif
2470
#endif
2471
2472
// Trait for checking if a type is a tl::expected
2473
template <class T>
2474
struct is_expected_impl : std::false_type {};
2475
template <class T, class E>
2476
struct is_expected_impl<expected<T, E>> : std::true_type {};
2477
template <class T>
2478
using is_expected = is_expected_impl<decay_t<T>>;
2479
2480
template <class T, class E, class U>
2481
using expected_enable_forward_value = detail::enable_if_t<
2482
    std::is_constructible<T, U&&>::value &&
2483
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2484
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2485
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2486
2487
template <class T, class E, class U, class G, class UR, class GR>
2488
using expected_enable_from_other = detail::enable_if_t<
2489
    std::is_constructible<T, UR>::value &&
2490
    std::is_constructible<E, GR>::value &&
2491
    !std::is_constructible<T, expected<U, G>&>::value &&
2492
    !std::is_constructible<T, expected<U, G>&&>::value &&
2493
    !std::is_constructible<T, const expected<U, G>&>::value &&
2494
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2495
    !std::is_convertible<expected<U, G>&, T>::value &&
2496
    !std::is_convertible<expected<U, G>&&, T>::value &&
2497
    !std::is_convertible<const expected<U, G>&, T>::value &&
2498
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2499
2500
template <class T, class U>
2501
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2502
2503
template <class T>
2504
using is_copy_constructible_or_void =
2505
    is_void_or<T, std::is_copy_constructible<T>>;
2506
2507
template <class T>
2508
using is_move_constructible_or_void =
2509
    is_void_or<T, std::is_move_constructible<T>>;
2510
2511
template <class T>
2512
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2513
2514
template <class T>
2515
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2516
2517
}  // namespace detail
2518
2519
namespace detail {
2520
struct no_init_t {};
2521
static constexpr no_init_t no_init{};
2522
2523
// Implements the storage of the values, and ensures that the destructor is
2524
// trivial if it can be.
2525
//
2526
// This specialization is for where neither `T` or `E` is trivially
2527
// destructible, so the destructors must be called on destruction of the
2528
// `expected`
2529
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2530
          bool = std::is_trivially_destructible<E>::value>
2531
struct expected_storage_base {
2532
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2533
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2534
2535
  template <class... Args,
2536
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2537
                nullptr>
2538
  constexpr expected_storage_base(in_place_t, Args&&... args)
2539
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2540
2541
  template <class U, class... Args,
2542
            detail::enable_if_t<std::is_constructible<
2543
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2544
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2545
                                  Args&&... args)
2546
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2547
  template <class... Args,
2548
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2549
                nullptr>
2550
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2551
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2552
2553
  template <class U, class... Args,
2554
            detail::enable_if_t<std::is_constructible<
2555
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2556
  constexpr explicit expected_storage_base(unexpect_t,
2557
                                           std::initializer_list<U> il,
2558
                                           Args&&... args)
2559
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2560
2561
  ~expected_storage_base() {
2562
    if (m_has_val) {
2563
      m_val.~T();
2564
    } else {
2565
      m_unexpect.~unexpected<E>();
2566
    }
2567
  }
2568
  union {
2569
    T m_val;
2570
    unexpected<E> m_unexpect;
2571
    char m_no_init;
2572
  };
2573
  bool m_has_val;
2574
};
2575
2576
// This specialization is for when both `T` and `E` are trivially-destructible,
2577
// so the destructor of the `expected` can be trivial.
2578
template <class T, class E>
2579
struct expected_storage_base<T, E, true, true> {
2580
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2581
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2582
2583
  template <class... Args,
2584
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2585
                nullptr>
2586
  constexpr expected_storage_base(in_place_t, Args&&... args)
2587
0
      : 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_
2588
2589
  template <class U, class... Args,
2590
            detail::enable_if_t<std::is_constructible<
2591
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2592
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2593
                                  Args&&... args)
2594
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2595
  template <class... Args,
2596
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2597
                nullptr>
2598
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2599
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2600
2601
  template <class U, class... Args,
2602
            detail::enable_if_t<std::is_constructible<
2603
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2604
  constexpr explicit expected_storage_base(unexpect_t,
2605
                                           std::initializer_list<U> il,
2606
                                           Args&&... args)
2607
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2608
2609
  ~expected_storage_base() = default;
2610
  union {
2611
    T m_val;
2612
    unexpected<E> m_unexpect;
2613
    char m_no_init;
2614
  };
2615
  bool m_has_val;
2616
};
2617
2618
// T is trivial, E is not.
2619
template <class T, class E>
2620
struct expected_storage_base<T, E, true, false> {
2621
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2622
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2623
      : m_no_init(), m_has_val(false) {}
2624
2625
  template <class... Args,
2626
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2627
                nullptr>
2628
  constexpr expected_storage_base(in_place_t, Args&&... args)
2629
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2630
2631
  template <class U, class... Args,
2632
            detail::enable_if_t<std::is_constructible<
2633
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2634
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2635
                                  Args&&... args)
2636
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2637
  template <class... Args,
2638
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2639
                nullptr>
2640
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2641
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2642
2643
  template <class U, class... Args,
2644
            detail::enable_if_t<std::is_constructible<
2645
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2646
  constexpr explicit expected_storage_base(unexpect_t,
2647
                                           std::initializer_list<U> il,
2648
                                           Args&&... args)
2649
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2650
2651
  ~expected_storage_base() {
2652
    if (!m_has_val) {
2653
      m_unexpect.~unexpected<E>();
2654
    }
2655
  }
2656
2657
  union {
2658
    T m_val;
2659
    unexpected<E> m_unexpect;
2660
    char m_no_init;
2661
  };
2662
  bool m_has_val;
2663
};
2664
2665
// E is trivial, T is not.
2666
template <class T, class E>
2667
struct expected_storage_base<T, E, false, true> {
2668
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2669
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2670
2671
  template <class... Args,
2672
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2673
                nullptr>
2674
  constexpr expected_storage_base(in_place_t, Args&&... args)
2675
0
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA1_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA2_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJRS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada23url_pattern_list_detail15compiled_routesENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
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_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJRS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
2676
2677
  template <class U, class... Args,
2678
            detail::enable_if_t<std::is_constructible<
2679
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2680
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2681
                                  Args&&... args)
2682
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2683
  template <class... Args,
2684
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2685
                nullptr>
2686
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2687
0
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada23url_pattern_list_detail15compiled_routesENS2_6errorsELb0ELb1EEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESC_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESF_
2688
2689
  template <class U, class... Args,
2690
            detail::enable_if_t<std::is_constructible<
2691
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2692
  constexpr explicit expected_storage_base(unexpect_t,
2693
                                           std::initializer_list<U> il,
2694
                                           Args&&... args)
2695
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2696
2697
0
  ~expected_storage_base() {
2698
0
    if (m_has_val) {
2699
0
      m_val.~T();
2700
0
    }
2701
0
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: 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()
Unexecuted instantiation: 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()
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()
Unexecuted instantiation: 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()
2702
  union {
2703
    T m_val;
2704
    unexpected<E> m_unexpect;
2705
    char m_no_init;
2706
  };
2707
  bool m_has_val;
2708
};
2709
2710
// `T` is `void`, `E` is trivially-destructible
2711
template <class E>
2712
struct expected_storage_base<void, E, false, true> {
2713
#if __GNUC__ <= 5
2714
// no constexpr for GCC 4/5 bug
2715
#else
2716
  TL_EXPECTED_MSVC2015_CONSTEXPR
2717
#endif
2718
  expected_storage_base() : m_has_val(true) {}
2719
2720
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2721
2722
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2723
2724
  template <class... Args,
2725
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2726
                nullptr>
2727
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2728
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2729
2730
  template <class U, class... Args,
2731
            detail::enable_if_t<std::is_constructible<
2732
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2733
  constexpr explicit expected_storage_base(unexpect_t,
2734
                                           std::initializer_list<U> il,
2735
                                           Args&&... args)
2736
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2737
2738
  ~expected_storage_base() = default;
2739
  struct dummy {};
2740
  union {
2741
    unexpected<E> m_unexpect;
2742
    dummy m_val;
2743
  };
2744
  bool m_has_val;
2745
};
2746
2747
// `T` is `void`, `E` is not trivially-destructible
2748
template <class E>
2749
struct expected_storage_base<void, E, false, false> {
2750
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2751
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2752
2753
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2754
2755
  template <class... Args,
2756
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2757
                nullptr>
2758
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2759
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2760
2761
  template <class U, class... Args,
2762
            detail::enable_if_t<std::is_constructible<
2763
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2764
  constexpr explicit expected_storage_base(unexpect_t,
2765
                                           std::initializer_list<U> il,
2766
                                           Args&&... args)
2767
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2768
2769
  ~expected_storage_base() {
2770
    if (!m_has_val) {
2771
      m_unexpect.~unexpected<E>();
2772
    }
2773
  }
2774
2775
  union {
2776
    unexpected<E> m_unexpect;
2777
    char m_dummy;
2778
  };
2779
  bool m_has_val;
2780
};
2781
2782
// This base class provides some handy member functions which can be used in
2783
// further derived classes
2784
template <class T, class E>
2785
struct expected_operations_base : expected_storage_base<T, E> {
2786
  using expected_storage_base<T, E>::expected_storage_base;
2787
2788
  template <class... Args>
2789
  void construct(Args&&... args) noexcept {
2790
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2791
    this->m_has_val = true;
2792
  }
2793
2794
  template <class Rhs>
2795
  // NOLINTNEXTLINE(bugprone-exception-escape)
2796
0
  void construct_with(Rhs&& rhs) noexcept {
2797
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2798
0
    this->m_has_val = true;
2799
0
  }
2800
2801
  template <class... Args>
2802
0
  void construct_error(Args&&... args) noexcept {
2803
0
    new (std::addressof(this->m_unexpect))
2804
0
        unexpected<E>(std::forward<Args>(args)...);
2805
0
    this->m_has_val = false;
2806
0
  }
2807
2808
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2809
2810
  // These assign overloads ensure that the most efficient assignment
2811
  // implementation is used while maintaining the strong exception guarantee.
2812
  // The problematic case is where rhs has a value, but *this does not.
2813
  //
2814
  // This overload handles the case where we can just copy-construct `T`
2815
  // directly into place without throwing.
2816
  template <class U = T,
2817
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2818
                nullptr>
2819
  void assign(const expected_operations_base& rhs) noexcept {
2820
    if (!this->m_has_val && rhs.m_has_val) {
2821
      geterr().~unexpected<E>();
2822
      construct(rhs.get());
2823
    } else {
2824
      assign_common(rhs);
2825
    }
2826
  }
2827
2828
  // This overload handles the case where we can attempt to create a copy of
2829
  // `T`, then no-throw move it into place if the copy was successful.
2830
  template <class U = T,
2831
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2832
                                std::is_nothrow_move_constructible<U>::value>* =
2833
                nullptr>
2834
  void assign(const expected_operations_base& rhs) noexcept {
2835
    if (!this->m_has_val && rhs.m_has_val) {
2836
      T tmp = rhs.get();
2837
      geterr().~unexpected<E>();
2838
      construct(std::move(tmp));
2839
    } else {
2840
      assign_common(rhs);
2841
    }
2842
  }
2843
2844
  // This overload is the worst-case, where we have to move-construct the
2845
  // unexpected value into temporary storage, then try to copy the T into place.
2846
  // If the construction succeeds, then everything is fine, but if it throws,
2847
  // then we move the old unexpected value back into place before rethrowing the
2848
  // exception.
2849
  template <class U = T,
2850
            detail::enable_if_t<
2851
                !std::is_nothrow_copy_constructible<U>::value &&
2852
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2853
  void assign(const expected_operations_base& rhs) {
2854
    if (!this->m_has_val && rhs.m_has_val) {
2855
      auto tmp = std::move(geterr());
2856
      geterr().~unexpected<E>();
2857
2858
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2859
      try {
2860
        construct(rhs.get());
2861
      } catch (...) {
2862
        geterr() = std::move(tmp);
2863
        throw;
2864
      }
2865
#else
2866
      construct(rhs.get());
2867
#endif
2868
    } else {
2869
      assign_common(rhs);
2870
    }
2871
  }
2872
2873
  // These overloads do the same as above, but for rvalues
2874
  template <class U = T,
2875
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2876
                nullptr>
2877
  void assign(expected_operations_base&& rhs) noexcept {
2878
    if (!this->m_has_val && rhs.m_has_val) {
2879
      geterr().~unexpected<E>();
2880
      construct(std::move(rhs).get());
2881
    } else {
2882
      assign_common(std::move(rhs));
2883
    }
2884
  }
2885
2886
  template <class U = T,
2887
            detail::enable_if_t<
2888
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2889
  void assign(expected_operations_base&& rhs) {
2890
    if (!this->m_has_val && rhs.m_has_val) {
2891
      auto tmp = std::move(geterr());
2892
      geterr().~unexpected<E>();
2893
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2894
      try {
2895
        construct(std::move(rhs).get());
2896
      } catch (...) {
2897
        geterr() = std::move(tmp);
2898
        throw;
2899
      }
2900
#else
2901
      construct(std::move(rhs).get());
2902
#endif
2903
    } else {
2904
      assign_common(std::move(rhs));
2905
    }
2906
  }
2907
2908
#else
2909
2910
  // If exceptions are disabled then we can just copy-construct
2911
  void assign(const expected_operations_base& rhs) noexcept {
2912
    if (!this->m_has_val && rhs.m_has_val) {
2913
      geterr().~unexpected<E>();
2914
      construct(rhs.get());
2915
    } else {
2916
      assign_common(rhs);
2917
    }
2918
  }
2919
2920
  void assign(expected_operations_base&& rhs) noexcept {
2921
    if (!this->m_has_val && rhs.m_has_val) {
2922
      geterr().~unexpected<E>();
2923
      construct(std::move(rhs).get());
2924
    } else {
2925
      assign_common(std::move(rhs));
2926
    }
2927
  }
2928
2929
#endif
2930
2931
  // The common part of move/copy assigning
2932
  template <class Rhs>
2933
  void assign_common(Rhs&& rhs) {
2934
    if (this->m_has_val) {
2935
      if (rhs.m_has_val) {
2936
        get() = std::forward<Rhs>(rhs).get();
2937
      } else {
2938
        destroy_val();
2939
        construct_error(std::forward<Rhs>(rhs).geterr());
2940
      }
2941
    } else {
2942
      if (!rhs.m_has_val) {
2943
        geterr() = std::forward<Rhs>(rhs).geterr();
2944
      }
2945
    }
2946
  }
2947
2948
0
  bool has_value() const { return this->m_has_val; }
2949
2950
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2951
0
  constexpr const T& get() const& { return this->m_val; }
2952
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2953
#ifndef TL_EXPECTED_NO_CONSTRR
2954
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2955
#endif
2956
2957
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2958
    return this->m_unexpect;
2959
  }
2960
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2961
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2962
    return std::move(this->m_unexpect);
2963
  }
2964
#ifndef TL_EXPECTED_NO_CONSTRR
2965
  constexpr const unexpected<E>&& geterr() const&& {
2966
    return std::move(this->m_unexpect);
2967
  }
2968
#endif
2969
2970
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2971
};
2972
2973
// This base class provides some handy member functions which can be used in
2974
// further derived classes
2975
template <class E>
2976
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2977
  using expected_storage_base<void, E>::expected_storage_base;
2978
2979
  template <class... Args>
2980
  void construct() noexcept {
2981
    this->m_has_val = true;
2982
  }
2983
2984
  // This function doesn't use its argument, but needs it so that code in
2985
  // levels above this can work independently of whether T is void
2986
  template <class Rhs>
2987
  void construct_with(Rhs&&) noexcept {
2988
    this->m_has_val = true;
2989
  }
2990
2991
  template <class... Args>
2992
  void construct_error(Args&&... args) noexcept {
2993
    new (std::addressof(this->m_unexpect))
2994
        unexpected<E>(std::forward<Args>(args)...);
2995
    this->m_has_val = false;
2996
  }
2997
2998
  template <class Rhs>
2999
  void assign(Rhs&& rhs) noexcept {
3000
    if (!this->m_has_val) {
3001
      if (rhs.m_has_val) {
3002
        geterr().~unexpected<E>();
3003
        construct();
3004
      } else {
3005
        geterr() = std::forward<Rhs>(rhs).geterr();
3006
      }
3007
    } else {
3008
      if (!rhs.m_has_val) {
3009
        construct_error(std::forward<Rhs>(rhs).geterr());
3010
      }
3011
    }
3012
  }
3013
3014
  bool has_value() const { return this->m_has_val; }
3015
3016
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
3017
    return this->m_unexpect;
3018
  }
3019
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
3020
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
3021
    return std::move(this->m_unexpect);
3022
  }
3023
#ifndef TL_EXPECTED_NO_CONSTRR
3024
  constexpr const unexpected<E>&& geterr() const&& {
3025
    return std::move(this->m_unexpect);
3026
  }
3027
#endif
3028
3029
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
3030
    // no-op
3031
  }
3032
};
3033
3034
// This class manages conditionally having a trivial copy constructor
3035
// This specialization is for when T and E are trivially copy constructible
3036
template <class T, class E,
3037
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
3038
                                   T)>::value &&
3039
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
3040
struct expected_copy_base : expected_operations_base<T, E> {
3041
  using expected_operations_base<T, E>::expected_operations_base;
3042
};
3043
3044
// This specialization is for when T or E are not trivially copy constructible
3045
template <class T, class E>
3046
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
3047
  using expected_operations_base<T, E>::expected_operations_base;
3048
3049
  expected_copy_base() = default;
3050
  expected_copy_base(const expected_copy_base& rhs)
3051
0
      : expected_operations_base<T, E>(no_init) {
3052
0
    if (rhs.has_value()) {
3053
0
      this->construct_with(rhs);
3054
0
    } else {
3055
0
      this->construct_error(rhs.geterr());
3056
0
    }
3057
0
  }
3058
3059
  expected_copy_base(expected_copy_base&& rhs) = default;
3060
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
3061
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
3062
};
3063
3064
// This class manages conditionally having a trivial move constructor
3065
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3066
// doesn't implement an analogue to std::is_trivially_move_constructible. We
3067
// have to make do with a non-trivial move constructor even if T is trivially
3068
// move constructible
3069
#ifndef TL_EXPECTED_GCC49
3070
template <class T, class E,
3071
          bool =
3072
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3073
              std::is_trivially_move_constructible<E>::value>
3074
struct expected_move_base : expected_copy_base<T, E> {
3075
  using expected_copy_base<T, E>::expected_copy_base;
3076
};
3077
#else
3078
template <class T, class E, bool = false>
3079
struct expected_move_base;
3080
#endif
3081
template <class T, class E>
3082
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3083
  using expected_copy_base<T, E>::expected_copy_base;
3084
3085
  expected_move_base() = default;
3086
0
  expected_move_base(const expected_move_base& rhs) = default;
3087
3088
  expected_move_base(expected_move_base&& rhs) noexcept(
3089
      std::is_nothrow_move_constructible<T>::value)
3090
      : expected_copy_base<T, E>(no_init) {
3091
    if (rhs.has_value()) {
3092
      this->construct_with(std::move(rhs));
3093
    } else {
3094
      this->construct_error(std::move(rhs.geterr()));
3095
    }
3096
  }
3097
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3098
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3099
};
3100
3101
// This class manages conditionally having a trivial copy assignment operator
3102
template <
3103
    class T, class E,
3104
    bool =
3105
        is_void_or<
3106
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3107
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3108
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3109
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3110
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3111
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3112
struct expected_copy_assign_base : expected_move_base<T, E> {
3113
  using expected_move_base<T, E>::expected_move_base;
3114
};
3115
3116
template <class T, class E>
3117
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3118
  using expected_move_base<T, E>::expected_move_base;
3119
3120
  expected_copy_assign_base() = default;
3121
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3122
3123
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3124
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3125
    this->assign(rhs);
3126
    return *this;
3127
  }
3128
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3129
      default;
3130
};
3131
3132
// This class manages conditionally having a trivial move assignment operator
3133
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3134
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3135
// to make do with a non-trivial move assignment operator even if T is trivially
3136
// move assignable
3137
#ifndef TL_EXPECTED_GCC49
3138
template <
3139
    class T, class E,
3140
    bool = is_void_or<
3141
               T, conjunction<std::is_trivially_destructible<T>,
3142
                              std::is_trivially_move_constructible<T>,
3143
                              std::is_trivially_move_assignable<T>>>::value &&
3144
           std::is_trivially_destructible<E>::value &&
3145
           std::is_trivially_move_constructible<E>::value &&
3146
           std::is_trivially_move_assignable<E>::value>
3147
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3148
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3149
};
3150
#else
3151
template <class T, class E, bool = false>
3152
struct expected_move_assign_base;
3153
#endif
3154
3155
template <class T, class E>
3156
struct expected_move_assign_base<T, E, false>
3157
    : expected_copy_assign_base<T, E> {
3158
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3159
3160
  expected_move_assign_base() = default;
3161
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3162
3163
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3164
3165
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3166
      default;
3167
3168
  expected_move_assign_base&
3169
  operator=(expected_move_assign_base&& rhs) noexcept(
3170
      std::is_nothrow_move_constructible<T>::value &&
3171
      std::is_nothrow_move_assignable<T>::value) {
3172
    this->assign(std::move(rhs));
3173
    return *this;
3174
  }
3175
};
3176
3177
// expected_delete_ctor_base will conditionally delete copy and move
3178
// constructors depending on whether T is copy/move constructible
3179
template <class T, class E,
3180
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3181
                             std::is_copy_constructible<E>::value),
3182
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3183
                             std::is_move_constructible<E>::value)>
3184
struct expected_delete_ctor_base {
3185
  expected_delete_ctor_base() = default;
3186
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3187
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3188
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3189
      default;
3190
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3191
      default;
3192
};
3193
3194
template <class T, class E>
3195
struct expected_delete_ctor_base<T, E, true, false> {
3196
  expected_delete_ctor_base() = default;
3197
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3198
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3199
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3200
      default;
3201
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3202
      default;
3203
};
3204
3205
template <class T, class E>
3206
struct expected_delete_ctor_base<T, E, false, true> {
3207
  expected_delete_ctor_base() = default;
3208
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3209
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3210
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3211
      default;
3212
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3213
      default;
3214
};
3215
3216
template <class T, class E>
3217
struct expected_delete_ctor_base<T, E, false, false> {
3218
  expected_delete_ctor_base() = default;
3219
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3220
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3221
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3222
      default;
3223
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3224
      default;
3225
};
3226
3227
// expected_delete_assign_base will conditionally delete copy and move
3228
// constructors depending on whether T and E are copy/move constructible +
3229
// assignable
3230
template <class T, class E,
3231
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3232
                             std::is_copy_constructible<E>::value &&
3233
                             is_copy_assignable_or_void<T>::value &&
3234
                             std::is_copy_assignable<E>::value),
3235
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3236
                             std::is_move_constructible<E>::value &&
3237
                             is_move_assignable_or_void<T>::value &&
3238
                             std::is_move_assignable<E>::value)>
3239
struct expected_delete_assign_base {
3240
  expected_delete_assign_base() = default;
3241
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3242
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3243
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3244
      default;
3245
  expected_delete_assign_base& operator=(
3246
      expected_delete_assign_base&&) noexcept = default;
3247
};
3248
3249
template <class T, class E>
3250
struct expected_delete_assign_base<T, E, true, false> {
3251
  expected_delete_assign_base() = default;
3252
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3253
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3254
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3255
      default;
3256
  expected_delete_assign_base& operator=(
3257
      expected_delete_assign_base&&) noexcept = delete;
3258
};
3259
3260
template <class T, class E>
3261
struct expected_delete_assign_base<T, E, false, true> {
3262
  expected_delete_assign_base() = default;
3263
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3264
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3265
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3266
      delete;
3267
  expected_delete_assign_base& operator=(
3268
      expected_delete_assign_base&&) noexcept = default;
3269
};
3270
3271
template <class T, class E>
3272
struct expected_delete_assign_base<T, E, false, false> {
3273
  expected_delete_assign_base() = default;
3274
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3275
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3276
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3277
      delete;
3278
  expected_delete_assign_base& operator=(
3279
      expected_delete_assign_base&&) noexcept = delete;
3280
};
3281
3282
// This is needed to be able to construct the expected_default_ctor_base which
3283
// follows, while still conditionally deleting the default constructor.
3284
struct default_constructor_tag {
3285
  explicit constexpr default_constructor_tag() = default;
3286
};
3287
3288
// expected_default_ctor_base will ensure that expected has a deleted default
3289
// consturctor if T is not default constructible.
3290
// This specialization is for when T is default constructible
3291
template <class T, class E,
3292
          bool Enable =
3293
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3294
struct expected_default_ctor_base {
3295
  constexpr expected_default_ctor_base() noexcept = default;
3296
  constexpr expected_default_ctor_base(
3297
      expected_default_ctor_base const&) noexcept = default;
3298
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3299
      default;
3300
  expected_default_ctor_base& operator=(
3301
      expected_default_ctor_base const&) noexcept = default;
3302
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3303
      default;
3304
3305
0
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_pattern_init, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Unexecuted instantiation: 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)
Unexecuted instantiation: 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)
Unexecuted instantiation: tl::detail::expected_default_ctor_base<ada::url_pattern_list_detail::compiled_routes, ada::errors, true>::expected_default_ctor_base(tl::detail::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)
Unexecuted instantiation: 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)
3306
};
3307
3308
// This specialization is for when T is not default constructible
3309
template <class T, class E>
3310
struct expected_default_ctor_base<T, E, false> {
3311
  constexpr expected_default_ctor_base() noexcept = delete;
3312
  constexpr expected_default_ctor_base(
3313
      expected_default_ctor_base const&) noexcept = default;
3314
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3315
      default;
3316
  expected_default_ctor_base& operator=(
3317
      expected_default_ctor_base const&) noexcept = default;
3318
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3319
      default;
3320
3321
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3322
};
3323
}  // namespace detail
3324
3325
template <class E>
3326
class bad_expected_access : public std::exception {
3327
 public:
3328
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3329
3330
0
  virtual const char* what() const noexcept override {
3331
0
    return "Bad expected access";
3332
0
  }
3333
3334
  const E& error() const& { return m_val; }
3335
  E& error() & { return m_val; }
3336
  const E&& error() const&& { return std::move(m_val); }
3337
  E&& error() && { return std::move(m_val); }
3338
3339
 private:
3340
  E m_val;
3341
};
3342
3343
/// An `expected<T, E>` object is an object that contains the storage for
3344
/// another object and manages the lifetime of this contained object `T`.
3345
/// Alternatively it could contain the storage for another unexpected object
3346
/// `E`. The contained object may not be initialized after the expected object
3347
/// has been initialized, and may not be destroyed before the expected object
3348
/// has been destroyed. The initialization state of the contained object is
3349
/// tracked by the expected object.
3350
template <class T, class E>
3351
class expected : private detail::expected_move_assign_base<T, E>,
3352
                 private detail::expected_delete_ctor_base<T, E>,
3353
                 private detail::expected_delete_assign_base<T, E>,
3354
                 private detail::expected_default_ctor_base<T, E> {
3355
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3356
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3357
                "T must not be in_place_t");
3358
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3359
                "T must not be unexpect_t");
3360
  static_assert(
3361
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3362
      "T must not be unexpected<E>");
3363
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3364
3365
0
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::valptr()
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()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::valptr()
3366
  const T* valptr() const { return std::addressof(this->m_val); }
3367
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3368
  const unexpected<E>* errptr() const {
3369
    return std::addressof(this->m_unexpect);
3370
  }
3371
3372
  template <class U = T,
3373
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3374
0
  TL_EXPECTED_11_CONSTEXPR U& val() {
3375
0
    return this->m_val;
3376
0
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEE3valIS3_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS9_v
3377
0
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::err()
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::err()
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::err()
3378
3379
  template <class U = T,
3380
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3381
  constexpr const U& val() const {
3382
    return this->m_val;
3383
  }
3384
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3385
3386
  using impl_base = detail::expected_move_assign_base<T, E>;
3387
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3388
3389
 public:
3390
  typedef T value_type;
3391
  typedef E error_type;
3392
  typedef unexpected<E> unexpected_type;
3393
3394
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3395
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3396
  template <class F>
3397
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3398
    return and_then_impl(*this, std::forward<F>(f));
3399
  }
3400
  template <class F>
3401
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3402
    return and_then_impl(std::move(*this), std::forward<F>(f));
3403
  }
3404
  template <class F>
3405
  constexpr auto and_then(F&& f) const& {
3406
    return and_then_impl(*this, std::forward<F>(f));
3407
  }
3408
3409
#ifndef TL_EXPECTED_NO_CONSTRR
3410
  template <class F>
3411
  constexpr auto and_then(F&& f) const&& {
3412
    return and_then_impl(std::move(*this), std::forward<F>(f));
3413
  }
3414
#endif
3415
3416
#else
3417
  template <class F>
3418
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3419
      std::declval<expected&>(), std::forward<F>(f))) {
3420
    return and_then_impl(*this, std::forward<F>(f));
3421
  }
3422
  template <class F>
3423
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3424
      std::declval<expected&&>(), std::forward<F>(f))) {
3425
    return and_then_impl(std::move(*this), std::forward<F>(f));
3426
  }
3427
  template <class F>
3428
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3429
      std::declval<expected const&>(), std::forward<F>(f))) {
3430
    return and_then_impl(*this, std::forward<F>(f));
3431
  }
3432
3433
#ifndef TL_EXPECTED_NO_CONSTRR
3434
  template <class F>
3435
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3436
      std::declval<expected const&&>(), std::forward<F>(f))) {
3437
    return and_then_impl(std::move(*this), std::forward<F>(f));
3438
  }
3439
#endif
3440
#endif
3441
3442
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3443
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3444
  template <class F>
3445
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3446
    return expected_map_impl(*this, std::forward<F>(f));
3447
  }
3448
  template <class F>
3449
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3450
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3451
  }
3452
  template <class F>
3453
  constexpr auto map(F&& f) const& {
3454
    return expected_map_impl(*this, std::forward<F>(f));
3455
  }
3456
  template <class F>
3457
  constexpr auto map(F&& f) const&& {
3458
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3459
  }
3460
#else
3461
  template <class F>
3462
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3463
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3464
    return expected_map_impl(*this, std::forward<F>(f));
3465
  }
3466
  template <class F>
3467
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3468
                                                      std::declval<F&&>()))
3469
  map(F&& f) && {
3470
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3471
  }
3472
  template <class F>
3473
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3474
                                       std::declval<F&&>()))
3475
  map(F&& f) const& {
3476
    return expected_map_impl(*this, std::forward<F>(f));
3477
  }
3478
3479
#ifndef TL_EXPECTED_NO_CONSTRR
3480
  template <class F>
3481
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3482
                                       std::declval<F&&>())) map(F&& f)
3483
      const&& {
3484
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3485
  }
3486
#endif
3487
#endif
3488
3489
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3490
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3491
  template <class F>
3492
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3493
    return expected_map_impl(*this, std::forward<F>(f));
3494
  }
3495
  template <class F>
3496
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3497
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3498
  }
3499
  template <class F>
3500
  constexpr auto transform(F&& f) const& {
3501
    return expected_map_impl(*this, std::forward<F>(f));
3502
  }
3503
  template <class F>
3504
  constexpr auto transform(F&& f) const&& {
3505
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3506
  }
3507
#else
3508
  template <class F>
3509
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3510
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3511
    return expected_map_impl(*this, std::forward<F>(f));
3512
  }
3513
  template <class F>
3514
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3515
                                                      std::declval<F&&>()))
3516
  transform(F&& f) && {
3517
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3518
  }
3519
  template <class F>
3520
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3521
                                       std::declval<F&&>()))
3522
  transform(F&& f) const& {
3523
    return expected_map_impl(*this, std::forward<F>(f));
3524
  }
3525
3526
#ifndef TL_EXPECTED_NO_CONSTRR
3527
  template <class F>
3528
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3529
                                       std::declval<F&&>())) transform(F&& f)
3530
      const&& {
3531
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3532
  }
3533
#endif
3534
#endif
3535
3536
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3537
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3538
  template <class F>
3539
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3540
    return map_error_impl(*this, std::forward<F>(f));
3541
  }
3542
  template <class F>
3543
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3544
    return map_error_impl(std::move(*this), std::forward<F>(f));
3545
  }
3546
  template <class F>
3547
  constexpr auto map_error(F&& f) const& {
3548
    return map_error_impl(*this, std::forward<F>(f));
3549
  }
3550
  template <class F>
3551
  constexpr auto map_error(F&& f) const&& {
3552
    return map_error_impl(std::move(*this), std::forward<F>(f));
3553
  }
3554
#else
3555
  template <class F>
3556
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3557
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3558
    return map_error_impl(*this, std::forward<F>(f));
3559
  }
3560
  template <class F>
3561
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3562
                                                   std::declval<F&&>()))
3563
  map_error(F&& f) && {
3564
    return map_error_impl(std::move(*this), std::forward<F>(f));
3565
  }
3566
  template <class F>
3567
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3568
                                    std::declval<F&&>()))
3569
  map_error(F&& f) const& {
3570
    return map_error_impl(*this, std::forward<F>(f));
3571
  }
3572
3573
#ifndef TL_EXPECTED_NO_CONSTRR
3574
  template <class F>
3575
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3576
                                    std::declval<F&&>())) map_error(F&& f)
3577
      const&& {
3578
    return map_error_impl(std::move(*this), std::forward<F>(f));
3579
  }
3580
#endif
3581
#endif
3582
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3583
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3584
  template <class F>
3585
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3586
    return map_error_impl(*this, std::forward<F>(f));
3587
  }
3588
  template <class F>
3589
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3590
    return map_error_impl(std::move(*this), std::forward<F>(f));
3591
  }
3592
  template <class F>
3593
  constexpr auto transform_error(F&& f) const& {
3594
    return map_error_impl(*this, std::forward<F>(f));
3595
  }
3596
  template <class F>
3597
  constexpr auto transform_error(F&& f) const&& {
3598
    return map_error_impl(std::move(*this), std::forward<F>(f));
3599
  }
3600
#else
3601
  template <class F>
3602
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3603
      std::declval<expected&>(),
3604
      std::declval<F&&>())) transform_error(F&& f) & {
3605
    return map_error_impl(*this, std::forward<F>(f));
3606
  }
3607
  template <class F>
3608
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3609
                                                   std::declval<F&&>()))
3610
  transform_error(F&& f) && {
3611
    return map_error_impl(std::move(*this), std::forward<F>(f));
3612
  }
3613
  template <class F>
3614
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3615
                                    std::declval<F&&>()))
3616
  transform_error(F&& f) const& {
3617
    return map_error_impl(*this, std::forward<F>(f));
3618
  }
3619
3620
#ifndef TL_EXPECTED_NO_CONSTRR
3621
  template <class F>
3622
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3623
                                    std::declval<F&&>())) transform_error(F&& f)
3624
      const&& {
3625
    return map_error_impl(std::move(*this), std::forward<F>(f));
3626
  }
3627
#endif
3628
#endif
3629
  template <class F>
3630
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3631
    return or_else_impl(*this, std::forward<F>(f));
3632
  }
3633
3634
  template <class F>
3635
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3636
    return or_else_impl(std::move(*this), std::forward<F>(f));
3637
  }
3638
3639
  template <class F>
3640
  expected constexpr or_else(F&& f) const& {
3641
    return or_else_impl(*this, std::forward<F>(f));
3642
  }
3643
3644
#ifndef TL_EXPECTED_NO_CONSTRR
3645
  template <class F>
3646
  expected constexpr or_else(F&& f) const&& {
3647
    return or_else_impl(std::move(*this), std::forward<F>(f));
3648
  }
3649
#endif
3650
  constexpr expected() = default;
3651
0
  constexpr expected(const expected& rhs) = default;
3652
  constexpr expected(expected&& rhs) = default;
3653
  expected& operator=(const expected& rhs) = default;
3654
  expected& operator=(expected&& rhs) = default;
3655
3656
  template <class... Args,
3657
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3658
                nullptr>
3659
  constexpr expected(in_place_t, Args&&... args)
3660
0
      : impl_base(in_place, std::forward<Args>(args)...),
3661
0
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA2_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IJRS8_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
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_
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IJRS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
3662
3663
  template <class U, class... Args,
3664
            detail::enable_if_t<std::is_constructible<
3665
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3666
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3667
      : impl_base(in_place, il, std::forward<Args>(args)...),
3668
        ctor_base(detail::default_constructor_tag{}) {}
3669
3670
  template <
3671
      class G = E,
3672
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3673
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3674
  explicit constexpr expected(const unexpected<G>& e)
3675
      : impl_base(unexpect, e.value()),
3676
        ctor_base(detail::default_constructor_tag{}) {}
3677
3678
  template <
3679
      class G = E,
3680
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3681
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3682
  constexpr expected(unexpected<G> const& e)
3683
      : impl_base(unexpect, e.value()),
3684
        ctor_base(detail::default_constructor_tag{}) {}
3685
3686
  template <
3687
      class G = E,
3688
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3689
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3690
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3691
      std::is_nothrow_constructible<E, G&&>::value)
3692
      : impl_base(unexpect, std::move(e.value())),
3693
        ctor_base(detail::default_constructor_tag{}) {}
3694
3695
  template <
3696
      class G = E,
3697
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3698
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3699
  constexpr expected(unexpected<G>&& e) noexcept(
3700
      std::is_nothrow_constructible<E, G&&>::value)
3701
0
      : impl_base(unexpect, std::move(e.value())),
3702
0
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Unexecuted instantiation: _ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Unexecuted instantiation: _ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IS4_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_OT_EE5valueEvE4typeELPv0ETnPNS8_IXsr3std14is_convertibleISA_S4_EE5valueEvE4typeELSE_0EEEONS_10unexpectedIS9_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IS8_TnPNS1_9enable_ifIXsr3std16is_constructibleIS8_OT_EE5valueEvE4typeELPv0ETnPNSB_IXsr3std14is_convertibleISD_S8_EE5valueEvE4typeELSH_0EEEONS_10unexpectedISC_EE
3703
3704
  template <class... Args,
3705
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3706
                nullptr>
3707
  constexpr explicit expected(unexpect_t, Args&&... args)
3708
      : impl_base(unexpect, std::forward<Args>(args)...),
3709
        ctor_base(detail::default_constructor_tag{}) {}
3710
3711
  template <class U, class... Args,
3712
            detail::enable_if_t<std::is_constructible<
3713
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3714
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3715
                              Args&&... args)
3716
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3717
        ctor_base(detail::default_constructor_tag{}) {}
3718
3719
  template <class U, class G,
3720
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3721
                                  std::is_convertible<G const&, E>::value)>* =
3722
                nullptr,
3723
            detail::expected_enable_from_other<T, E, U, G, const U&,
3724
                                               const G&>* = nullptr>
3725
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3726
      : ctor_base(detail::default_constructor_tag{}) {
3727
    if (rhs.has_value()) {
3728
      this->construct(*rhs);
3729
    } else {
3730
      this->construct_error(rhs.error());
3731
    }
3732
  }
3733
3734
  template <
3735
      class U, class G,
3736
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3737
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3738
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3739
          nullptr>
3740
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3741
      : ctor_base(detail::default_constructor_tag{}) {
3742
    if (rhs.has_value()) {
3743
      this->construct(*rhs);
3744
    } else {
3745
      this->construct_error(rhs.error());
3746
    }
3747
  }
3748
3749
  template <
3750
      class U, class G,
3751
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3752
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3753
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3754
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3755
      : ctor_base(detail::default_constructor_tag{}) {
3756
    if (rhs.has_value()) {
3757
      this->construct(std::move(*rhs));
3758
    } else {
3759
      this->construct_error(std::move(rhs.error()));
3760
    }
3761
  }
3762
3763
  template <
3764
      class U, class G,
3765
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3766
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3767
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3768
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3769
      : ctor_base(detail::default_constructor_tag{}) {
3770
    if (rhs.has_value()) {
3771
      this->construct(std::move(*rhs));
3772
    } else {
3773
      this->construct_error(std::move(rhs.error()));
3774
    }
3775
  }
3776
3777
  template <class U = T,
3778
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3779
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3780
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3781
      : expected(in_place, std::forward<U>(v)) {}
3782
3783
  template <class U = T,
3784
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3785
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3786
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3787
0
      : expected(in_place, std::forward<U>(v)) {}
Unexecuted instantiation: _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_
Unexecuted instantiation: _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_
Unexecuted instantiation: _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_
Unexecuted instantiation: _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_
Unexecuted instantiation: _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_
Unexecuted instantiation: _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_
Unexecuted instantiation: _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_
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S3_EE5valueEvE4typeELPv0ETnPNS8_IXaaaaaasr3std16is_constructibleIS3_SA_EE5valuentsr3std7is_sameINS7_5decayIS9_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS5_SH_EE5valuentsr3std7is_sameINS_10unexpectedIS4_EESH_EE5valueEvE4typeELSE_0EEESA_
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_
Unexecuted instantiation: _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_
3788
3789
  template <
3790
      class U = T, class G = T,
3791
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3792
          nullptr,
3793
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3794
      detail::enable_if_t<
3795
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3796
           !detail::conjunction<std::is_scalar<T>,
3797
                                std::is_same<T, detail::decay_t<U>>>::value &&
3798
           std::is_constructible<T, U>::value &&
3799
           std::is_assignable<G&, U>::value &&
3800
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3801
  expected& operator=(U&& v) {
3802
    if (has_value()) {
3803
      val() = std::forward<U>(v);
3804
    } else {
3805
      err().~unexpected<E>();
3806
      ::new (valptr()) T(std::forward<U>(v));
3807
      this->m_has_val = true;
3808
    }
3809
3810
    return *this;
3811
  }
3812
3813
  template <
3814
      class U = T, class G = T,
3815
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3816
          nullptr,
3817
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3818
      detail::enable_if_t<
3819
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3820
           !detail::conjunction<std::is_scalar<T>,
3821
                                std::is_same<T, detail::decay_t<U>>>::value &&
3822
           std::is_constructible<T, U>::value &&
3823
           std::is_assignable<G&, U>::value &&
3824
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3825
  expected& operator=(U&& v) {
3826
    if (has_value()) {
3827
      val() = std::forward<U>(v);
3828
    } else {
3829
      auto tmp = std::move(err());
3830
      err().~unexpected<E>();
3831
3832
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3833
      try {
3834
        ::new (valptr()) T(std::forward<U>(v));
3835
        this->m_has_val = true;
3836
      } catch (...) {
3837
        err() = std::move(tmp);
3838
        throw;
3839
      }
3840
#else
3841
      ::new (valptr()) T(std::forward<U>(v));
3842
      this->m_has_val = true;
3843
#endif
3844
    }
3845
3846
    return *this;
3847
  }
3848
3849
  template <class G = E,
3850
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3851
                                std::is_assignable<G&, G>::value>* = nullptr>
3852
  expected& operator=(const unexpected<G>& rhs) {
3853
    if (!has_value()) {
3854
      err() = rhs;
3855
    } else {
3856
      this->destroy_val();
3857
      ::new (errptr()) unexpected<E>(rhs);
3858
      this->m_has_val = false;
3859
    }
3860
3861
    return *this;
3862
  }
3863
3864
  template <class G = E,
3865
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3866
                                std::is_move_assignable<G>::value>* = nullptr>
3867
  expected& operator=(unexpected<G>&& rhs) noexcept {
3868
    if (!has_value()) {
3869
      err() = std::move(rhs);
3870
    } else {
3871
      this->destroy_val();
3872
      ::new (errptr()) unexpected<E>(std::move(rhs));
3873
      this->m_has_val = false;
3874
    }
3875
3876
    return *this;
3877
  }
3878
3879
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3880
                               T, Args&&...>::value>* = nullptr>
3881
  void emplace(Args&&... args) {
3882
    if (has_value()) {
3883
      val().~T();
3884
    } else {
3885
      err().~unexpected<E>();
3886
      this->m_has_val = true;
3887
    }
3888
    ::new (valptr()) T(std::forward<Args>(args)...);
3889
  }
3890
3891
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3892
                               T, Args&&...>::value>* = nullptr>
3893
  void emplace(Args&&... args) {
3894
    if (has_value()) {
3895
      val().~T();
3896
      ::new (valptr()) T(std::forward<Args>(args)...);
3897
    } else {
3898
      auto tmp = std::move(err());
3899
      err().~unexpected<E>();
3900
3901
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3902
      try {
3903
        ::new (valptr()) T(std::forward<Args>(args)...);
3904
        this->m_has_val = true;
3905
      } catch (...) {
3906
        err() = std::move(tmp);
3907
        throw;
3908
      }
3909
#else
3910
      ::new (valptr()) T(std::forward<Args>(args)...);
3911
      this->m_has_val = true;
3912
#endif
3913
    }
3914
  }
3915
3916
  template <class U, class... Args,
3917
            detail::enable_if_t<std::is_nothrow_constructible<
3918
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3919
  void emplace(std::initializer_list<U> il, Args&&... args) {
3920
    if (has_value()) {
3921
      T t(il, std::forward<Args>(args)...);
3922
      val() = std::move(t);
3923
    } else {
3924
      err().~unexpected<E>();
3925
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3926
      this->m_has_val = true;
3927
    }
3928
  }
3929
3930
  template <class U, class... Args,
3931
            detail::enable_if_t<!std::is_nothrow_constructible<
3932
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3933
  void emplace(std::initializer_list<U> il, Args&&... args) {
3934
    if (has_value()) {
3935
      T t(il, std::forward<Args>(args)...);
3936
      val() = std::move(t);
3937
    } else {
3938
      auto tmp = std::move(err());
3939
      err().~unexpected<E>();
3940
3941
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3942
      try {
3943
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3944
        this->m_has_val = true;
3945
      } catch (...) {
3946
        err() = std::move(tmp);
3947
        throw;
3948
      }
3949
#else
3950
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3951
      this->m_has_val = true;
3952
#endif
3953
    }
3954
  }
3955
3956
 private:
3957
  using t_is_void = std::true_type;
3958
  using t_is_not_void = std::false_type;
3959
  using t_is_nothrow_move_constructible = std::true_type;
3960
  using move_constructing_t_can_throw = std::false_type;
3961
  using e_is_nothrow_move_constructible = std::true_type;
3962
  using move_constructing_e_can_throw = std::false_type;
3963
3964
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3965
    // swapping void is a no-op
3966
  }
3967
3968
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3969
    using std::swap;
3970
    swap(val(), rhs.val());
3971
  }
3972
3973
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3974
      std::is_nothrow_move_constructible<E>::value) {
3975
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3976
    rhs.err().~unexpected_type();
3977
    std::swap(this->m_has_val, rhs.m_has_val);
3978
  }
3979
3980
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3981
    swap_where_only_one_has_value_and_t_is_not_void(
3982
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3983
        typename std::is_nothrow_move_constructible<E>::type{});
3984
  }
3985
3986
  void swap_where_only_one_has_value_and_t_is_not_void(
3987
      expected& rhs, t_is_nothrow_move_constructible,
3988
      e_is_nothrow_move_constructible) noexcept {
3989
    auto temp = std::move(val());
3990
    val().~T();
3991
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3992
    rhs.err().~unexpected_type();
3993
    ::new (rhs.valptr()) T(std::move(temp));
3994
    std::swap(this->m_has_val, rhs.m_has_val);
3995
  }
3996
3997
  void swap_where_only_one_has_value_and_t_is_not_void(
3998
      expected& rhs, t_is_nothrow_move_constructible,
3999
      move_constructing_e_can_throw) {
4000
    auto temp = std::move(val());
4001
    val().~T();
4002
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4003
    try {
4004
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
4005
      rhs.err().~unexpected_type();
4006
      ::new (rhs.valptr()) T(std::move(temp));
4007
      std::swap(this->m_has_val, rhs.m_has_val);
4008
    } catch (...) {
4009
      val() = std::move(temp);
4010
      throw;
4011
    }
4012
#else
4013
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
4014
    rhs.err().~unexpected_type();
4015
    ::new (rhs.valptr()) T(std::move(temp));
4016
    std::swap(this->m_has_val, rhs.m_has_val);
4017
#endif
4018
  }
4019
4020
  void swap_where_only_one_has_value_and_t_is_not_void(
4021
      expected& rhs, move_constructing_t_can_throw,
4022
      e_is_nothrow_move_constructible) {
4023
    auto temp = std::move(rhs.err());
4024
    rhs.err().~unexpected_type();
4025
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4026
    try {
4027
      ::new (rhs.valptr()) T(std::move(val()));
4028
      val().~T();
4029
      ::new (errptr()) unexpected_type(std::move(temp));
4030
      std::swap(this->m_has_val, rhs.m_has_val);
4031
    } catch (...) {
4032
      rhs.err() = std::move(temp);
4033
      throw;
4034
    }
4035
#else
4036
    ::new (rhs.valptr()) T(std::move(val()));
4037
    val().~T();
4038
    ::new (errptr()) unexpected_type(std::move(temp));
4039
    std::swap(this->m_has_val, rhs.m_has_val);
4040
#endif
4041
  }
4042
4043
 public:
4044
  template <class OT = T, class OE = E>
4045
  detail::enable_if_t<detail::is_swappable<OT>::value &&
4046
                      detail::is_swappable<OE>::value &&
4047
                      (std::is_nothrow_move_constructible<OT>::value ||
4048
                       std::is_nothrow_move_constructible<OE>::value)>
4049
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
4050
                               detail::is_nothrow_swappable<T>::value &&
4051
                               std::is_nothrow_move_constructible<E>::value &&
4052
                               detail::is_nothrow_swappable<E>::value) {
4053
    if (has_value() && rhs.has_value()) {
4054
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
4055
    } else if (!has_value() && rhs.has_value()) {
4056
      rhs.swap(*this);
4057
    } else if (has_value()) {
4058
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
4059
    } else {
4060
      using std::swap;
4061
      swap(err(), rhs.err());
4062
    }
4063
  }
4064
4065
  constexpr const T* operator->() const {
4066
    TL_ASSERT(has_value());
4067
    return valptr();
4068
  }
4069
0
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4070
0
    TL_ASSERT(has_value());
4071
0
    return valptr();
4072
0
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::operator->()
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->()
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator->()
4073
4074
  template <class U = T,
4075
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4076
  constexpr const U& operator*() const& {
4077
    TL_ASSERT(has_value());
4078
    return val();
4079
  }
4080
  template <class U = T,
4081
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4082
0
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4083
0
    TL_ASSERT(has_value());
4084
0
    return val();
4085
0
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Unexecuted instantiation: _ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Unexecuted instantiation: _ZNR2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEdeIS3_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS9_v
4086
  template <class U = T,
4087
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4088
  constexpr const U&& operator*() const&& {
4089
    TL_ASSERT(has_value());
4090
    return std::move(val());
4091
  }
4092
  template <class U = T,
4093
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4094
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4095
    TL_ASSERT(has_value());
4096
    return std::move(val());
4097
  }
4098
4099
0
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::has_value() const
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
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::has_value() const
4100
0
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::operator bool() const
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::operator bool() const
4101
4102
  template <class U = T,
4103
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4104
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4105
    if (!has_value())
4106
      detail::throw_exception(bad_expected_access<E>(err().value()));
4107
    return val();
4108
  }
4109
  template <class U = T,
4110
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4111
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4112
0
    if (!has_value())
4113
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4114
0
    return val();
4115
0
  }
4116
  template <class U = T,
4117
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4118
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4119
    if (!has_value())
4120
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4121
    return std::move(val());
4122
  }
4123
  template <class U = T,
4124
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4125
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4126
    if (!has_value())
4127
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4128
    return std::move(val());
4129
  }
4130
4131
  constexpr const E& error() const& {
4132
    TL_ASSERT(!has_value());
4133
    return err().value();
4134
  }
4135
0
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4136
0
    TL_ASSERT(!has_value());
4137
0
    return err().value();
4138
0
  }
Unexecuted instantiation: tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
Unexecuted instantiation: tl::expected<ada::url_pattern_init, ada::errors>::error() &
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::error() &
4139
  constexpr const E&& error() const&& {
4140
    TL_ASSERT(!has_value());
4141
    return std::move(err().value());
4142
  }
4143
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4144
    TL_ASSERT(!has_value());
4145
    return std::move(err().value());
4146
  }
4147
4148
  template <class U>
4149
  constexpr T value_or(U&& v) const& {
4150
    static_assert(std::is_copy_constructible<T>::value &&
4151
                      std::is_convertible<U&&, T>::value,
4152
                  "T must be copy-constructible and convertible to from U&&");
4153
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4154
  }
4155
  template <class U>
4156
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4157
    static_assert(std::is_move_constructible<T>::value &&
4158
                      std::is_convertible<U&&, T>::value,
4159
                  "T must be move-constructible and convertible to from U&&");
4160
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4161
  }
4162
};
4163
4164
namespace detail {
4165
template <class Exp>
4166
using exp_t = typename detail::decay_t<Exp>::value_type;
4167
template <class Exp>
4168
using err_t = typename detail::decay_t<Exp>::error_type;
4169
template <class Exp, class Ret>
4170
using ret_t = expected<Ret, err_t<Exp>>;
4171
4172
#ifdef TL_EXPECTED_CXX14
4173
template <class Exp, class F,
4174
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4175
          class Ret = decltype(detail::invoke(std::declval<F>(),
4176
                                              *std::declval<Exp>()))>
4177
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4178
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4179
4180
  return exp.has_value()
4181
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4182
             : Ret(unexpect, std::forward<Exp>(exp).error());
4183
}
4184
4185
template <class Exp, class F,
4186
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4187
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4188
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4189
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4190
4191
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4192
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4193
}
4194
#else
4195
template <class>
4196
struct TC;
4197
template <class Exp, class F,
4198
          class Ret = decltype(detail::invoke(std::declval<F>(),
4199
                                              *std::declval<Exp>())),
4200
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4201
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4202
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4203
4204
  return exp.has_value()
4205
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4206
             : Ret(unexpect, std::forward<Exp>(exp).error());
4207
}
4208
4209
template <class Exp, class F,
4210
          class Ret = decltype(detail::invoke(std::declval<F>())),
4211
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4212
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4213
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4214
4215
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4216
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4217
}
4218
#endif
4219
4220
#ifdef TL_EXPECTED_CXX14
4221
template <class Exp, class F,
4222
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4223
          class Ret = decltype(detail::invoke(std::declval<F>(),
4224
                                              *std::declval<Exp>())),
4225
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4226
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4227
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4228
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4229
                                                 *std::forward<Exp>(exp)))
4230
                         : result(unexpect, std::forward<Exp>(exp).error());
4231
}
4232
4233
template <class Exp, class F,
4234
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4235
          class Ret = decltype(detail::invoke(std::declval<F>(),
4236
                                              *std::declval<Exp>())),
4237
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4238
auto expected_map_impl(Exp&& exp, F&& f) {
4239
  using result = expected<void, err_t<Exp>>;
4240
  if (exp.has_value()) {
4241
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4242
    return result();
4243
  }
4244
4245
  return result(unexpect, std::forward<Exp>(exp).error());
4246
}
4247
4248
template <class Exp, class F,
4249
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4250
          class Ret = decltype(detail::invoke(std::declval<F>())),
4251
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4252
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4253
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4254
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4255
                         : result(unexpect, std::forward<Exp>(exp).error());
4256
}
4257
4258
template <class Exp, class F,
4259
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4260
          class Ret = decltype(detail::invoke(std::declval<F>())),
4261
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4262
auto expected_map_impl(Exp&& exp, F&& f) {
4263
  using result = expected<void, err_t<Exp>>;
4264
  if (exp.has_value()) {
4265
    detail::invoke(std::forward<F>(f));
4266
    return result();
4267
  }
4268
4269
  return result(unexpect, std::forward<Exp>(exp).error());
4270
}
4271
#else
4272
template <class Exp, class F,
4273
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4274
          class Ret = decltype(detail::invoke(std::declval<F>(),
4275
                                              *std::declval<Exp>())),
4276
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4277
4278
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4279
    -> ret_t<Exp, detail::decay_t<Ret>> {
4280
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4281
4282
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4283
                                                 *std::forward<Exp>(exp)))
4284
                         : result(unexpect, std::forward<Exp>(exp).error());
4285
}
4286
4287
template <class Exp, class F,
4288
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4289
          class Ret = decltype(detail::invoke(std::declval<F>(),
4290
                                              *std::declval<Exp>())),
4291
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4292
4293
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4294
  if (exp.has_value()) {
4295
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4296
    return {};
4297
  }
4298
4299
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4300
}
4301
4302
template <class Exp, class F,
4303
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4304
          class Ret = decltype(detail::invoke(std::declval<F>())),
4305
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4306
4307
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4308
    -> ret_t<Exp, detail::decay_t<Ret>> {
4309
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4310
4311
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4312
                         : result(unexpect, std::forward<Exp>(exp).error());
4313
}
4314
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
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4319
4320
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4321
  if (exp.has_value()) {
4322
    detail::invoke(std::forward<F>(f));
4323
    return {};
4324
  }
4325
4326
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4327
}
4328
#endif
4329
4330
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4331
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4332
template <class Exp, class F,
4333
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4334
          class Ret = decltype(detail::invoke(std::declval<F>(),
4335
                                              std::declval<Exp>().error())),
4336
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4337
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4338
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4339
  return exp.has_value()
4340
             ? result(*std::forward<Exp>(exp))
4341
             : result(unexpect, detail::invoke(std::forward<F>(f),
4342
                                               std::forward<Exp>(exp).error()));
4343
}
4344
template <class Exp, class F,
4345
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4346
          class Ret = decltype(detail::invoke(std::declval<F>(),
4347
                                              std::declval<Exp>().error())),
4348
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4349
auto map_error_impl(Exp&& exp, F&& f) {
4350
  using result = expected<exp_t<Exp>, monostate>;
4351
  if (exp.has_value()) {
4352
    return result(*std::forward<Exp>(exp));
4353
  }
4354
4355
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4356
  return result(unexpect, monostate{});
4357
}
4358
template <class Exp, class F,
4359
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4360
          class Ret = decltype(detail::invoke(std::declval<F>(),
4361
                                              std::declval<Exp>().error())),
4362
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4363
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4364
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4365
  return exp.has_value()
4366
             ? result()
4367
             : result(unexpect, detail::invoke(std::forward<F>(f),
4368
                                               std::forward<Exp>(exp).error()));
4369
}
4370
template <class Exp, class F,
4371
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4372
          class Ret = decltype(detail::invoke(std::declval<F>(),
4373
                                              std::declval<Exp>().error())),
4374
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4375
auto map_error_impl(Exp&& exp, F&& f) {
4376
  using result = expected<exp_t<Exp>, monostate>;
4377
  if (exp.has_value()) {
4378
    return result();
4379
  }
4380
4381
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4382
  return result(unexpect, monostate{});
4383
}
4384
#else
4385
template <class Exp, class F,
4386
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4387
          class Ret = decltype(detail::invoke(std::declval<F>(),
4388
                                              std::declval<Exp>().error())),
4389
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4390
constexpr auto map_error_impl(Exp&& exp, F&& f)
4391
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4392
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4393
4394
  return exp.has_value()
4395
             ? result(*std::forward<Exp>(exp))
4396
             : result(unexpect, detail::invoke(std::forward<F>(f),
4397
                                               std::forward<Exp>(exp).error()));
4398
}
4399
4400
template <class Exp, class F,
4401
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4402
          class Ret = decltype(detail::invoke(std::declval<F>(),
4403
                                              std::declval<Exp>().error())),
4404
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4405
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4406
  using result = expected<exp_t<Exp>, monostate>;
4407
  if (exp.has_value()) {
4408
    return result(*std::forward<Exp>(exp));
4409
  }
4410
4411
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4412
  return result(unexpect, monostate{});
4413
}
4414
4415
template <class Exp, class F,
4416
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4417
          class Ret = decltype(detail::invoke(std::declval<F>(),
4418
                                              std::declval<Exp>().error())),
4419
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4420
constexpr auto map_error_impl(Exp&& exp, F&& f)
4421
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4422
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4423
4424
  return exp.has_value()
4425
             ? result()
4426
             : result(unexpect, detail::invoke(std::forward<F>(f),
4427
                                               std::forward<Exp>(exp).error()));
4428
}
4429
4430
template <class Exp, class F,
4431
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4432
          class Ret = decltype(detail::invoke(std::declval<F>(),
4433
                                              std::declval<Exp>().error())),
4434
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4435
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4436
  using result = expected<exp_t<Exp>, monostate>;
4437
  if (exp.has_value()) {
4438
    return result();
4439
  }
4440
4441
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4442
  return result(unexpect, monostate{});
4443
}
4444
#endif
4445
4446
#ifdef TL_EXPECTED_CXX14
4447
template <class Exp, class F,
4448
          class Ret = decltype(detail::invoke(std::declval<F>(),
4449
                                              std::declval<Exp>().error())),
4450
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4451
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4452
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4453
  return exp.has_value() ? std::forward<Exp>(exp)
4454
                         : detail::invoke(std::forward<F>(f),
4455
                                          std::forward<Exp>(exp).error());
4456
}
4457
4458
template <class Exp, class F,
4459
          class Ret = decltype(detail::invoke(std::declval<F>(),
4460
                                              std::declval<Exp>().error())),
4461
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4462
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4463
  return exp.has_value() ? std::forward<Exp>(exp)
4464
                         : (detail::invoke(std::forward<F>(f),
4465
                                           std::forward<Exp>(exp).error()),
4466
                            std::forward<Exp>(exp));
4467
}
4468
#else
4469
template <class Exp, class F,
4470
          class Ret = decltype(detail::invoke(std::declval<F>(),
4471
                                              std::declval<Exp>().error())),
4472
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4473
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4474
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4475
  return exp.has_value() ? std::forward<Exp>(exp)
4476
                         : detail::invoke(std::forward<F>(f),
4477
                                          std::forward<Exp>(exp).error());
4478
}
4479
4480
template <class Exp, class F,
4481
          class Ret = decltype(detail::invoke(std::declval<F>(),
4482
                                              std::declval<Exp>().error())),
4483
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4484
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4485
  return exp.has_value() ? std::forward<Exp>(exp)
4486
                         : (detail::invoke(std::forward<F>(f),
4487
                                           std::forward<Exp>(exp).error()),
4488
                            std::forward<Exp>(exp));
4489
}
4490
#endif
4491
}  // namespace detail
4492
4493
template <class T, class E, class U, class F>
4494
constexpr bool operator==(const expected<T, E>& lhs,
4495
                          const expected<U, F>& rhs) {
4496
  return (lhs.has_value() != rhs.has_value())
4497
             ? false
4498
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4499
}
4500
template <class T, class E, class U, class F>
4501
constexpr bool operator!=(const expected<T, E>& lhs,
4502
                          const expected<U, F>& rhs) {
4503
  return (lhs.has_value() != rhs.has_value())
4504
             ? true
4505
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4506
}
4507
template <class E, class F>
4508
constexpr bool operator==(const expected<void, E>& lhs,
4509
                          const expected<void, F>& rhs) {
4510
  return (lhs.has_value() != rhs.has_value())
4511
             ? false
4512
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4513
}
4514
template <class E, class F>
4515
constexpr bool operator!=(const expected<void, E>& lhs,
4516
                          const expected<void, F>& rhs) {
4517
  return (lhs.has_value() != rhs.has_value())
4518
             ? true
4519
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4520
}
4521
4522
template <class T, class E, class U>
4523
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4524
  return x.has_value() ? *x == v : false;
4525
}
4526
template <class T, class E, class U>
4527
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4528
  return x.has_value() ? *x == v : false;
4529
}
4530
template <class T, class E, class U>
4531
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4532
  return x.has_value() ? *x != v : true;
4533
}
4534
template <class T, class E, class U>
4535
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4536
  return x.has_value() ? *x != v : true;
4537
}
4538
4539
template <class T, class E>
4540
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4541
  return x.has_value() ? false : x.error() == e.value();
4542
}
4543
template <class T, class E>
4544
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4545
  return x.has_value() ? false : x.error() == e.value();
4546
}
4547
template <class T, class E>
4548
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4549
  return x.has_value() ? true : x.error() != e.value();
4550
}
4551
template <class T, class E>
4552
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4553
  return x.has_value() ? true : x.error() != e.value();
4554
}
4555
4556
template <class T, class E,
4557
          detail::enable_if_t<(std::is_void<T>::value ||
4558
                               std::is_move_constructible<T>::value) &&
4559
                              detail::is_swappable<T>::value &&
4560
                              std::is_move_constructible<E>::value &&
4561
                              detail::is_swappable<E>::value>* = nullptr>
4562
void swap(expected<T, E>& lhs,
4563
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4564
  lhs.swap(rhs);
4565
}
4566
}  // namespace tl
4567
4568
#endif
4569
/* end file include/ada/expected.h */
4570
4571
/* begin file include/ada/url_pattern_regex.h */
4572
/**
4573
 * @file url_search_params.h
4574
 * @brief Declaration for the URL Search Params
4575
 */
4576
#ifndef ADA_URL_PATTERN_REGEX_H
4577
#define ADA_URL_PATTERN_REGEX_H
4578
4579
#include <concepts>
4580
#include <optional>
4581
#include <string>
4582
#include <string_view>
4583
#include <vector>
4584
4585
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4586
#include <regex>
4587
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4588
4589
#if ADA_INCLUDE_URL_PATTERN
4590
namespace ada::url_pattern_regex {
4591
4592
/**
4593
 * @brief Concept defining the interface for regex engine providers used by
4594
 * URLPattern.
4595
 *
4596
 * A regex provider supplies the regular expression engine used for
4597
 * pattern compilation and matching.
4598
 *
4599
 * Requirements:
4600
 * - typename T::regex_type: The compiled regex object type.
4601
 * - T::create_instance(pattern, ignore_case): Compiles the regex pattern
4602
 * string. Returns std::nullopt if the pattern is invalid or cannot be compiled.
4603
 * - T::regex_search(input, pattern): Executes a regex search against the input,
4604
 *     returning a vector of capture group values (with std::nullopt for
4605
 * unmatched optional groups) or std::nullopt if the input does not match.
4606
 * - T::regex_match(input, pattern): Returns true if the input matches the
4607
 * pattern, or false otherwise.
4608
 *
4609
 * Exception contract:
4610
 * Provider methods may throw exceptions (for example, on allocation failure,
4611
 * input length limits, complexity limits, or internal engine errors). In
4612
 * environments where C++ exceptions are enabled, exceptions thrown by the
4613
 * provider will propagate out of URLPattern functions (such as fast_test,
4614
 * fast_match, test, exec, match, etc.). Providers that prefer non-throwing
4615
 * operation should handle internal errors and return false or std::nullopt.
4616
 */
4617
template <typename T>
4618
concept regex_concept = requires(T t, std::string_view pattern,
4619
                                 bool ignore_case, std::string_view input) {
4620
  // Ensure the class has a type alias 'regex_type'
4621
  typename T::regex_type;
4622
4623
  // Function to create a regex instance
4624
  {
4625
    T::create_instance(pattern, ignore_case)
4626
  } -> std::same_as<std::optional<typename T::regex_type>>;
4627
4628
  // Function to perform regex search
4629
  {
4630
    T::regex_search(input, std::declval<typename T::regex_type&>())
4631
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4632
4633
  // Function to match regex pattern
4634
  {
4635
    T::regex_match(input, std::declval<typename T::regex_type&>())
4636
  } -> std::same_as<bool>;
4637
4638
  // Copy constructor
4639
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4640
4641
  // Move constructor
4642
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4643
};
4644
4645
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4646
class std_regex_provider final {
4647
 public:
4648
  std_regex_provider() = default;
4649
  using regex_type = std::regex;
4650
  static std::optional<regex_type> create_instance(std::string_view pattern,
4651
                                                   bool ignore_case);
4652
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4653
      std::string_view input, const regex_type& pattern);
4654
  static bool regex_match(std::string_view input, const regex_type& pattern);
4655
};
4656
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4657
4658
}  // namespace ada::url_pattern_regex
4659
#endif  // ADA_INCLUDE_URL_PATTERN
4660
#endif  // ADA_URL_PATTERN_REGEX_H
4661
/* end file include/ada/url_pattern_regex.h */
4662
/* begin file include/ada/url_pattern_init.h */
4663
/**
4664
 * @file url_pattern_init.h
4665
 * @brief Declaration for the url_pattern_init implementation.
4666
 */
4667
#ifndef ADA_URL_PATTERN_INIT_H
4668
#define ADA_URL_PATTERN_INIT_H
4669
4670
/* begin file include/ada/errors.h */
4671
/**
4672
 * @file errors.h
4673
 * @brief Error type definitions for URL parsing.
4674
 *
4675
 * Defines the error codes that can be returned when URL parsing fails.
4676
 */
4677
#ifndef ADA_ERRORS_H
4678
#define ADA_ERRORS_H
4679
4680
#include <cstdint>
4681
namespace ada {
4682
/**
4683
 * @brief Error codes for URL parsing operations.
4684
 *
4685
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4686
 */
4687
enum class errors : uint8_t {
4688
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4689
};
4690
}  // namespace ada
4691
#endif  // ADA_ERRORS_H
4692
/* end file include/ada/errors.h */
4693
4694
#include <concepts>
4695
#include <cstdint>
4696
#include <iostream>
4697
#include <optional>
4698
#include <string>
4699
#include <string_view>
4700
4701
#if ADA_TESTING
4702
#include <iostream>
4703
#endif  // ADA_TESTING
4704
4705
#if ADA_INCLUDE_URL_PATTERN
4706
namespace ada {
4707
4708
// Important: C++20 allows us to use concept rather than `using` or `typedef
4709
// and allows functions with second argument, which is optional (using either
4710
// std::nullopt or a parameter with default value)
4711
template <typename F>
4712
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4713
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4714
};
4715
4716
// A structure providing matching patterns for individual components
4717
// of a URL. When a URLPattern is created, or when a URLPattern is
4718
// used to match or test against a URL, the input can be given as
4719
// either a string or a URLPatternInit struct. If a string is given,
4720
// it will be parsed to create a URLPatternInit. The URLPatternInit
4721
// API is defined as part of the URLPattern specification.
4722
// All provided strings must be valid UTF-8.
4723
struct url_pattern_init {
4724
  enum class process_type : uint8_t {
4725
    url,
4726
    pattern,
4727
  };
4728
4729
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4730
0
    switch (type) {
4731
0
      case process_type::url:
4732
0
        return os << "url";
4733
0
      case process_type::pattern:
4734
0
        return os << "pattern";
4735
0
      default:
4736
0
        return os << "unknown";
4737
0
    }
4738
0
  }
4739
4740
  // All strings must be valid UTF-8.
4741
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4742
  static tl::expected<url_pattern_init, errors> process(
4743
      const url_pattern_init& init, process_type type,
4744
      std::optional<std::string_view> protocol = std::nullopt,
4745
      std::optional<std::string_view> username = std::nullopt,
4746
      std::optional<std::string_view> password = std::nullopt,
4747
      std::optional<std::string_view> hostname = std::nullopt,
4748
      std::optional<std::string_view> port = std::nullopt,
4749
      std::optional<std::string_view> pathname = std::nullopt,
4750
      std::optional<std::string_view> search = std::nullopt,
4751
      std::optional<std::string_view> hash = std::nullopt);
4752
4753
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4754
  static tl::expected<std::string, errors> process_protocol(
4755
      std::string_view value, process_type type);
4756
4757
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4758
  static tl::expected<std::string, errors> process_username(
4759
      std::string_view value, process_type type);
4760
4761
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4762
  static tl::expected<std::string, errors> process_password(
4763
      std::string_view value, process_type type);
4764
4765
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4766
  static tl::expected<std::string, errors> process_hostname(
4767
      std::string_view value, process_type type);
4768
4769
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4770
  static tl::expected<std::string, errors> process_port(
4771
      std::string_view port, std::string_view protocol, process_type type);
4772
4773
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4774
  static tl::expected<std::string, errors> process_pathname(
4775
      std::string_view value, std::string_view protocol, process_type type);
4776
4777
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4778
  static tl::expected<std::string, errors> process_search(
4779
      std::string_view value, process_type type);
4780
4781
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4782
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4783
                                                        process_type type);
4784
4785
#if ADA_TESTING
4786
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4787
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4788
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4789
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4790
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4791
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4792
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4793
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4794
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4795
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4796
  }
4797
#endif  // ADA_TESTING
4798
4799
  bool operator==(const url_pattern_init&) const;
4800
  // If present, must be valid UTF-8.
4801
  std::optional<std::string> protocol{};
4802
  // If present, must be valid UTF-8.
4803
  std::optional<std::string> username{};
4804
  // If present, must be valid UTF-8.
4805
  std::optional<std::string> password{};
4806
  // If present, must be valid UTF-8.
4807
  std::optional<std::string> hostname{};
4808
  // If present, must be valid UTF-8.
4809
  std::optional<std::string> port{};
4810
  // If present, must be valid UTF-8.
4811
  std::optional<std::string> pathname{};
4812
  // If present, must be valid UTF-8.
4813
  std::optional<std::string> search{};
4814
  // If present, must be valid UTF-8.
4815
  std::optional<std::string> hash{};
4816
  // If present, must be valid UTF-8.
4817
  std::optional<std::string> base_url{};
4818
};
4819
}  // namespace ada
4820
#endif  // ADA_INCLUDE_URL_PATTERN
4821
#endif  // ADA_URL_PATTERN_INIT_H
4822
/* end file include/ada/url_pattern_init.h */
4823
4824
/** @private Forward declarations */
4825
namespace ada {
4826
struct url_aggregator;
4827
struct url;
4828
#if ADA_INCLUDE_URL_PATTERN
4829
template <url_pattern_regex::regex_concept regex_provider>
4830
class url_pattern;
4831
struct url_pattern_options;
4832
#endif  // ADA_INCLUDE_URL_PATTERN
4833
enum class errors : uint8_t;
4834
}  // namespace ada
4835
4836
/**
4837
 * @namespace ada::parser
4838
 * @brief Internal URL parsing implementation.
4839
 *
4840
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4841
 * Standard. These functions are used internally by `ada::parse()`.
4842
 */
4843
namespace ada::parser {
4844
/**
4845
 * Parses a URL string into a URL object.
4846
 *
4847
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4848
 *
4849
 * @param user_input The URL string to parse (must be valid UTF-8).
4850
 * @param base_url Optional base URL for resolving relative URLs.
4851
 *
4852
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4853
 *         succeeded.
4854
 *
4855
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4856
 */
4857
template <typename result_type = url_aggregator>
4858
result_type parse_url(std::string_view user_input,
4859
                      const result_type* base_url = nullptr);
4860
4861
extern template url_aggregator parse_url<url_aggregator>(
4862
    std::string_view user_input, const url_aggregator* base_url);
4863
extern template url parse_url<url>(std::string_view user_input,
4864
                                   const url* base_url);
4865
4866
template <typename result_type = url_aggregator, bool store_values = true>
4867
result_type parse_url_impl(std::string_view user_input,
4868
                           const result_type* base_url = nullptr);
4869
4870
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4871
    std::string_view user_input, const url_aggregator* base_url);
4872
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4873
    std::string_view user_input, const url_aggregator* base_url);
4874
extern template url parse_url_impl<url, true>(std::string_view user_input,
4875
                                              const url* base_url);
4876
4877
/** @private */
4878
template <class result_type>
4879
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4880
4881
/** @private */
4882
template <class result_type>
4883
bool finish_simple_absolute_with_port(std::string_view input, result_type& out,
4884
                                      ada::scheme::type scheme_type,
4885
                                      uint32_t protocol_end, size_t host_start,
4886
                                      size_t host_end, size_t host_len,
4887
                                      bool has_upper);
4888
4889
/** @private */
4890
template <class result_type>
4891
bool try_parse_simple_relative(std::string_view input, const result_type& base,
4892
                               result_type& out);
4893
4894
#if ADA_INCLUDE_URL_PATTERN
4895
template <url_pattern_regex::regex_concept regex_provider>
4896
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4897
    std::variant<std::string_view, url_pattern_init>&& input,
4898
    const std::string_view* base_url, const url_pattern_options* options);
4899
#endif  // ADA_INCLUDE_URL_PATTERN
4900
4901
}  // namespace ada::parser
4902
4903
#endif  // ADA_PARSER_H
4904
/* end file include/ada/parser.h */
4905
/* begin file include/ada/parser-inl.h */
4906
/**
4907
 * @file parser-inl.h
4908
 */
4909
#ifndef ADA_PARSER_INL_H
4910
#define ADA_PARSER_INL_H
4911
4912
/* begin file include/ada/url_pattern.h */
4913
/**
4914
 * @file url_pattern.h
4915
 * @brief URLPattern API implementation.
4916
 *
4917
 * This header provides the URLPattern API as specified by the WHATWG URL
4918
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4919
 * wildcards and named groups, similar to how regular expressions match strings.
4920
 *
4921
 * @see https://urlpattern.spec.whatwg.org/
4922
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4923
 */
4924
#ifndef ADA_URL_PATTERN_H
4925
#define ADA_URL_PATTERN_H
4926
4927
/* begin file include/ada/implementation.h */
4928
/**
4929
 * @file implementation.h
4930
 * @brief User-facing functions for URL parsing and manipulation.
4931
 *
4932
 * This header provides the primary public API for parsing URLs in Ada.
4933
 * It includes the main `ada::parse()` function which is the recommended
4934
 * entry point for most users.
4935
 *
4936
 * @see https://url.spec.whatwg.org/#api
4937
 */
4938
#ifndef ADA_IMPLEMENTATION_H
4939
#define ADA_IMPLEMENTATION_H
4940
4941
#include <cstdint>
4942
#include <optional>
4943
#include <span>
4944
#include <string>
4945
#include <string_view>
4946
#include <variant>
4947
4948
/* begin file include/ada/url.h */
4949
/**
4950
 * @file url.h
4951
 * @brief Declaration for the `ada::url` class.
4952
 *
4953
 * This file contains the `ada::url` struct which represents a parsed URL
4954
 * using separate `std::string` instances for each component. This
4955
 * representation is more flexible but uses more memory than `url_aggregator`.
4956
 *
4957
 * @see url_aggregator.h for a more memory-efficient alternative
4958
 */
4959
#ifndef ADA_URL_H
4960
#define ADA_URL_H
4961
4962
#include <algorithm>
4963
#include <cstddef>
4964
#include <cstdint>
4965
#include <optional>
4966
#include <ostream>
4967
#include <string>
4968
#include <string_view>
4969
4970
/* begin file include/ada/url_components.h */
4971
/**
4972
 * @file url_components.h
4973
 * @brief URL component offset representation for url_aggregator.
4974
 *
4975
 * This file defines the `url_components` struct which stores byte offsets
4976
 * into a URL string buffer. It is used internally by `url_aggregator` to
4977
 * efficiently locate URL components without storing separate strings.
4978
 */
4979
#ifndef ADA_URL_COMPONENTS_H
4980
#define ADA_URL_COMPONENTS_H
4981
4982
#include <cstdint>
4983
#include <string>
4984
4985
namespace ada {
4986
4987
/**
4988
 * @brief Stores byte offsets for URL components within a buffer.
4989
 *
4990
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4991
 * of each URL component within a single string buffer. This enables efficient
4992
 * component extraction without additional memory allocations.
4993
 *
4994
 * Component layout in a URL:
4995
 * ```
4996
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4997
 *       |     |    |          | ^^^^|       |   |
4998
 *       |     |    |          | |   |       |   `----- hash_start
4999
 *       |     |    |          | |   |       `--------- search_start
5000
 *       |     |    |          | |   `----------------- pathname_start
5001
 *       |     |    |          | `--------------------- port
5002
 *       |     |    |          `----------------------- host_end
5003
 *       |     |    `---------------------------------- host_start
5004
 *       |     `--------------------------------------- username_end
5005
 *       `--------------------------------------------- protocol_end
5006
 * ```
5007
 *
5008
 * @note The 32-bit offsets limit URLs to 4GB in length.
5009
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
5010
 * present.
5011
 */
5012
struct url_components {
5013
  /** Sentinel value indicating a component is not present. */
5014
  constexpr static uint32_t omitted = uint32_t(-1);
5015
5016
0
  url_components() = default;
5017
  url_components(const url_components& u) = default;
5018
  url_components(url_components&& u) noexcept = default;
5019
  url_components& operator=(url_components&& u) noexcept = default;
5020
  url_components& operator=(const url_components& u) = default;
5021
  ~url_components() = default;
5022
5023
  /** Offset of the end of the protocol/scheme (position of ':'). */
5024
  uint32_t protocol_end{0};
5025
5026
  /**
5027
   * Offset of the end of the username.
5028
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
5029
   */
5030
  uint32_t username_end{0};
5031
5032
  /** Offset of the start of the host. */
5033
  uint32_t host_start{0};
5034
5035
  /** Offset of the end of the host. */
5036
  uint32_t host_end{0};
5037
5038
  /** Port number, or `omitted` if no port is specified. */
5039
  uint32_t port{omitted};
5040
5041
  /** Offset of the start of the pathname. */
5042
  uint32_t pathname_start{0};
5043
5044
  /** Offset of the '?' starting the query, or `omitted` if no query. */
5045
  uint32_t search_start{omitted};
5046
5047
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
5048
  uint32_t hash_start{omitted};
5049
5050
  /**
5051
   * Validates that offsets are in ascending order and consistent.
5052
   * Useful for debugging to detect internal corruption.
5053
   * @return `true` if offsets are consistent, `false` otherwise.
5054
   */
5055
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
5056
5057
  /**
5058
   * Returns a JSON string representation of the offsets for debugging.
5059
   * @return A JSON-formatted string with all offset values.
5060
   */
5061
  [[nodiscard]] std::string to_string() const;
5062
5063
};  // struct url_components
5064
}  // namespace ada
5065
#endif
5066
/* end file include/ada/url_components.h */
5067
5068
namespace ada {
5069
5070
struct url_aggregator;
5071
5072
// namespace parser {
5073
// template <typename result_type>
5074
// result_type parse_url(std::string_view user_input,
5075
//                       const result_type* base_url = nullptr);
5076
// template <typename result_type, bool store_values>
5077
// result_type parse_url_impl(std::string_view user_input,
5078
//                            const result_type* base_url = nullptr);
5079
// }
5080
5081
/**
5082
 * @brief Represents a parsed URL with individual string components.
5083
 *
5084
 * The `url` struct stores each URL component (scheme, username, password,
5085
 * host, port, path, query, fragment) as a separate `std::string`. This
5086
 * provides flexibility but incurs more memory allocations compared to
5087
 * `url_aggregator`.
5088
 *
5089
 * **When to use `ada::url`:**
5090
 * - When you need to frequently modify individual URL components
5091
 * - When you want independent ownership of component strings
5092
 *
5093
 * **When to use `ada::url_aggregator` instead:**
5094
 * - For read-mostly operations on parsed URLs
5095
 * - When memory efficiency is important
5096
 * - When you only need string_view access to components
5097
 *
5098
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
5099
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
5100
 *
5101
 * @see url_aggregator For a more memory-efficient URL representation
5102
 * @see https://url.spec.whatwg.org/#url-representation
5103
 */
5104
struct url : url_base {
5105
0
  url() = default;
5106
0
  url(const url& u) = default;
5107
0
  url(url&& u) noexcept = default;
5108
0
  url& operator=(url&& u) noexcept = default;
5109
0
  url& operator=(const url& u) = default;
5110
0
  ~url() override = default;
5111
5112
  // Fields are ordered so that the most frequently accessed components
5113
  // tend to occupy earlier cache lines and remain close together in memory.
5114
  //
5115
  // Note: The exact object layout (including cache-line boundaries, byte
5116
  // offsets, and member sizes) is implementation- and platform-dependent.
5117
  // This ordering expresses an intent for better cache locality but does not
5118
  // guarantee any specific in-memory layout.
5119
5120
  /**
5121
   * @private
5122
   * A URL's host is null or a host. It is initially null.
5123
   */
5124
  std::optional<std::string> host{};
5125
5126
  /**
5127
   * @private
5128
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5129
   * strings, usually identifying a location.
5130
   */
5131
  std::string path{};
5132
5133
  /**
5134
   * @private
5135
   * A URL's query is either null or an ASCII string. It is initially null.
5136
   */
5137
  std::optional<std::string> query{};
5138
5139
  /**
5140
   * @private
5141
   * A URL's fragment is either null or an ASCII string that can be used for
5142
   * further processing on the resource the URL's other components identify. It
5143
   * is initially null.
5144
   */
5145
  std::optional<std::string> hash{};
5146
5147
  /**
5148
   * @private
5149
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5150
   * networking port. It is initially null.
5151
   */
5152
  std::optional<uint16_t> port{};
5153
5154
  /**
5155
   * @private
5156
   * A URL's username is an ASCII string identifying a username. It is initially
5157
   * the empty string.
5158
   */
5159
  std::string username{};
5160
5161
  /**
5162
   * @private
5163
   * A URL's password is an ASCII string identifying a password. It is initially
5164
   * the empty string.
5165
   */
5166
  std::string password{};
5167
5168
  /**
5169
   * Checks if the URL has an empty hostname (host is set but empty string).
5170
   * @return `true` if host exists but is empty, `false` otherwise.
5171
   */
5172
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5173
5174
  /**
5175
   * Checks if the URL has a non-default port explicitly specified.
5176
   * @return `true` if a port is present, `false` otherwise.
5177
   */
5178
  [[nodiscard]] inline bool has_port() const noexcept;
5179
5180
  /**
5181
   * Checks if the URL has a hostname (including empty hostnames).
5182
   * @return `true` if host is present, `false` otherwise.
5183
   */
5184
  [[nodiscard]] inline bool has_hostname() const noexcept;
5185
5186
  /**
5187
   * Validates whether the hostname is a valid domain according to RFC 1034.
5188
   * Checks that the domain and its labels have valid lengths (max 255 octets
5189
   * total, max 63 octets per label).
5190
   * @return `true` if the domain is valid, `false` otherwise.
5191
   */
5192
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5193
5194
  /**
5195
   * Returns a JSON string representation of this URL for debugging.
5196
   * @return A JSON-formatted string with all URL components.
5197
   */
5198
  [[nodiscard]] std::string to_string() const override;
5199
5200
  /**
5201
   * Returns the full serialized URL (the href).
5202
   * @return The complete URL string (allocates a new string).
5203
   * @see https://url.spec.whatwg.org/#dom-url-href
5204
   */
5205
  [[nodiscard]] ada_really_inline std::string get_href() const;
5206
5207
  /**
5208
   * Returns the byte length of the serialized URL without allocating a string.
5209
   * @return Size of the href in bytes.
5210
   */
5211
  [[nodiscard]] size_t get_href_size() const noexcept;
5212
5213
  /**
5214
   * Returns the URL's origin as a string (scheme + host + port for special
5215
   * URLs).
5216
   * @return A newly allocated string containing the serialized origin.
5217
   * @see https://url.spec.whatwg.org/#concept-url-origin
5218
   */
5219
  [[nodiscard]] std::string get_origin() const override;
5220
5221
  /**
5222
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5223
   * @return A newly allocated string with the protocol.
5224
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5225
   */
5226
  [[nodiscard]] std::string get_protocol() const;
5227
5228
  /**
5229
   * Returns the URL's host and port (e.g., "example.com:8080").
5230
   * If no port is set, returns just the host. Returns empty string if no host.
5231
   * @return A newly allocated string with host:port.
5232
   * @see https://url.spec.whatwg.org/#dom-url-host
5233
   */
5234
  [[nodiscard]] std::string get_host() const;
5235
5236
  /**
5237
   * Returns the URL's hostname (without port).
5238
   * Returns empty string if no host is set.
5239
   * @return A newly allocated string with the hostname.
5240
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5241
   */
5242
  [[nodiscard]] std::string get_hostname() const;
5243
5244
  /**
5245
   * Returns the URL's path component.
5246
   * @return A string_view pointing to the path.
5247
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5248
   */
5249
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5250
5251
  /**
5252
   * Returns the byte length of the pathname without creating a string.
5253
   * @return Size of the pathname in bytes.
5254
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5255
   */
5256
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5257
5258
  /**
5259
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5260
   * Returns empty string if no query is set.
5261
   * @return A newly allocated string with the search/query.
5262
   * @see https://url.spec.whatwg.org/#dom-url-search
5263
   */
5264
  [[nodiscard]] std::string get_search() const;
5265
5266
  /**
5267
   * Returns the URL's username component.
5268
   * @return A constant reference to the username string.
5269
   * @see https://url.spec.whatwg.org/#dom-url-username
5270
   */
5271
  [[nodiscard]] const std::string& get_username() const noexcept;
5272
5273
  /**
5274
   * Sets the URL's username, percent-encoding special characters.
5275
   * @param input The new username value.
5276
   * @return `true` on success, `false` if the URL cannot have credentials.
5277
   * @see https://url.spec.whatwg.org/#dom-url-username
5278
   */
5279
  bool set_username(std::string_view input);
5280
5281
  /**
5282
   * Sets the URL's password, percent-encoding special characters.
5283
   * @param input The new password value.
5284
   * @return `true` on success, `false` if the URL cannot have credentials.
5285
   * @see https://url.spec.whatwg.org/#dom-url-password
5286
   */
5287
  bool set_password(std::string_view input);
5288
5289
  /**
5290
   * Sets the URL's port from a string (e.g., "8080").
5291
   * @param input The port string. Empty string removes the port.
5292
   * @return `true` on success, `false` if the URL cannot have a port.
5293
   * @see https://url.spec.whatwg.org/#dom-url-port
5294
   */
5295
  bool set_port(std::string_view input);
5296
5297
  /**
5298
   * Sets the URL's fragment/hash (the part after '#').
5299
   * @param input The new hash value (with or without leading '#').
5300
   * @see https://url.spec.whatwg.org/#dom-url-hash
5301
   */
5302
  void set_hash(std::string_view input);
5303
5304
  /**
5305
   * Sets the URL's query string (the part after '?').
5306
   * @param input The new query value (with or without leading '?').
5307
   * @see https://url.spec.whatwg.org/#dom-url-search
5308
   */
5309
  void set_search(std::string_view input);
5310
5311
  /**
5312
   * Sets the URL's pathname.
5313
   * @param input The new path value.
5314
   * @return `true` on success, `false` if the URL has an opaque path.
5315
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5316
   */
5317
  bool set_pathname(std::string_view input);
5318
5319
  /**
5320
   * Sets the URL's host (hostname and optionally port).
5321
   * @param input The new host value (e.g., "example.com:8080").
5322
   * @return `true` on success, `false` if parsing fails.
5323
   * @see https://url.spec.whatwg.org/#dom-url-host
5324
   */
5325
  bool set_host(std::string_view input);
5326
5327
  /**
5328
   * Sets the URL's hostname (without port).
5329
   * @param input The new hostname value.
5330
   * @return `true` on success, `false` if parsing fails.
5331
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5332
   */
5333
  bool set_hostname(std::string_view input);
5334
5335
  /**
5336
   * Sets the URL's protocol/scheme.
5337
   * @param input The new protocol (with or without trailing ':').
5338
   * @return `true` on success, `false` if the scheme is invalid.
5339
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5340
   */
5341
  bool set_protocol(std::string_view input);
5342
5343
  /**
5344
   * Replaces the entire URL by parsing a new href string.
5345
   * @param input The new URL string to parse.
5346
   * @return `true` on success, `false` if parsing fails.
5347
   * @see https://url.spec.whatwg.org/#dom-url-href
5348
   */
5349
  bool set_href(std::string_view input);
5350
5351
  /**
5352
   * Returns the URL's password component.
5353
   * @return A constant reference to the password string.
5354
   * @see https://url.spec.whatwg.org/#dom-url-password
5355
   */
5356
  [[nodiscard]] const std::string& get_password() const noexcept;
5357
5358
  /**
5359
   * Returns the URL's port as a string (e.g., "8080").
5360
   * Returns empty string if no port is set.
5361
   * @return A newly allocated string with the port.
5362
   * @see https://url.spec.whatwg.org/#dom-url-port
5363
   */
5364
  [[nodiscard]] std::string get_port() const;
5365
5366
  /**
5367
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5368
   * Returns empty string if no fragment is set.
5369
   * @return A newly allocated string with the hash.
5370
   * @see https://url.spec.whatwg.org/#dom-url-hash
5371
   */
5372
  [[nodiscard]] std::string get_hash() const;
5373
5374
  /**
5375
   * Checks if the URL has credentials (non-empty username or password).
5376
   * @return `true` if username or password is non-empty, `false` otherwise.
5377
   */
5378
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5379
5380
  /**
5381
   * Returns the URL component offsets for efficient serialization.
5382
   *
5383
   * The components represent byte offsets into the serialized URL:
5384
   * ```
5385
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5386
   *       |     |    |          | ^^^^|       |   |
5387
   *       |     |    |          | |   |       |   `----- hash_start
5388
   *       |     |    |          | |   |       `--------- search_start
5389
   *       |     |    |          | |   `----------------- pathname_start
5390
   *       |     |    |          | `--------------------- port
5391
   *       |     |    |          `----------------------- host_end
5392
   *       |     |    `---------------------------------- host_start
5393
   *       |     `--------------------------------------- username_end
5394
   *       `--------------------------------------------- protocol_end
5395
   * ```
5396
   * @return A newly constructed url_components struct.
5397
   * @see https://github.com/servo/rust-url
5398
   */
5399
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5400
5401
  /**
5402
   * Checks if the URL has a fragment/hash component.
5403
   * @return `true` if hash is present, `false` otherwise.
5404
   */
5405
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5406
5407
  /**
5408
   * Checks if the URL has a query/search component.
5409
   * @return `true` if query is present, `false` otherwise.
5410
   */
5411
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5412
5413
 private:
5414
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5415
                                                   const ada::url*);
5416
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5417
      std::string_view, const ada::url_aggregator*);
5418
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5419
      ada::url& url);
5420
5421
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5422
                                                              const ada::url*);
5423
  friend ada::url_aggregator ada::parser::parse_url_impl<
5424
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5425
  friend ada::url_aggregator ada::parser::parse_url_impl<
5426
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5427
  template <class result_type>
5428
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5429
                                                     result_type&);
5430
  template <class result_type>
5431
  friend bool ada::parser::finish_simple_absolute_with_port(
5432
      std::string_view, result_type&, ada::scheme::type, uint32_t, size_t,
5433
      size_t, size_t, bool);
5434
  template <class result_type>
5435
  friend bool ada::parser::try_parse_simple_relative(std::string_view,
5436
                                                     const result_type&,
5437
                                                     result_type&);
5438
5439
  inline void update_unencoded_base_hash(std::string_view input);
5440
  inline void update_base_hostname(std::string_view input);
5441
  inline void update_base_search(std::string_view input,
5442
                                 const uint8_t query_percent_encode_set[]);
5443
  inline void update_base_search(std::optional<std::string>&& input);
5444
  inline void update_base_pathname(std::string_view input);
5445
  inline void update_base_username(std::string_view input);
5446
  inline void update_base_password(std::string_view input);
5447
  inline void update_base_port(std::optional<uint16_t> input);
5448
5449
  /**
5450
   * Returns true if growing href by input_len (worst case) could exceed
5451
   * get_max_input_length(), meaning a rollback snapshot is actually needed.
5452
   */
5453
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
5454
5455
  /**
5456
   * Sets the host or hostname according to override condition.
5457
   * Return true on success.
5458
   * @see https://url.spec.whatwg.org/#hostname-state
5459
   */
5460
  template <bool override_hostname = false>
5461
  bool set_host_or_hostname(std::string_view input);
5462
5463
  /**
5464
   * Return true on success.
5465
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5466
   */
5467
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5468
5469
  /**
5470
   * Return true on success.
5471
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5472
   */
5473
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5474
5475
  /**
5476
   * Return true on success.
5477
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5478
   */
5479
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5480
5481
  /**
5482
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5483
   * be used to dispatch a URL for further processing after parsing. It is
5484
   * initially the empty string. We only set non_special_scheme when the scheme
5485
   * is non-special, otherwise we avoid constructing string.
5486
   *
5487
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5488
   * typically do not need to store them in each url instance.
5489
   */
5490
  std::string non_special_scheme{};
5491
5492
  /**
5493
   * A URL cannot have a username/password/port if its host is null or the empty
5494
   * string, or its scheme is "file".
5495
   */
5496
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5497
5498
  ada_really_inline size_t parse_port(
5499
      std::string_view view, bool check_trailing_content) noexcept override;
5500
5501
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5502
0
    return this->parse_port(view, false);
5503
0
  }
5504
5505
  /**
5506
   * Parse the host from the provided input. We assume that
5507
   * the input does not contain spaces or tabs. Control
5508
   * characters and spaces are not trimmed (they should have
5509
   * been removed if needed).
5510
   * Return true on success.
5511
   * @see https://url.spec.whatwg.org/#host-parsing
5512
   */
5513
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5514
5515
  template <bool has_state_override = false>
5516
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5517
5518
  constexpr void clear_pathname() override;
5519
  constexpr void clear_search() override;
5520
  constexpr void set_protocol_as_file();
5521
5522
  /**
5523
   * Parse the path from the provided input.
5524
   * Return true on success. Control characters not
5525
   * trimmed from the ends (they should have
5526
   * been removed if needed).
5527
   *
5528
   * The input is expected to be UTF-8.
5529
   *
5530
   * @see https://url.spec.whatwg.org/
5531
   */
5532
  ada_really_inline void parse_path(std::string_view input);
5533
5534
  /**
5535
   * Set the scheme for this URL. The provided scheme should be a valid
5536
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5537
   * have no spurious trailing or leading content.
5538
   */
5539
  inline void set_scheme(std::string&& new_scheme) noexcept;
5540
5541
  /**
5542
   * Take the scheme from another URL. The scheme string is moved from the
5543
   * provided url.
5544
   */
5545
  constexpr void copy_scheme(ada::url&& u);
5546
5547
  /**
5548
   * Take the scheme from another URL. The scheme string is copied from the
5549
   * provided url.
5550
   */
5551
  constexpr void copy_scheme(const ada::url& u);
5552
5553
};  // struct url
5554
5555
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5556
}  // namespace ada
5557
5558
#endif  // ADA_URL_H
5559
/* end file include/ada/url.h */
5560
5561
namespace ada {
5562
5563
/**
5564
 * Result type for URL parsing operations.
5565
 *
5566
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5567
 * error. This allows for exception-free error handling.
5568
 *
5569
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5570
 *
5571
 * @example
5572
 * ```cpp
5573
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5574
 * if (result) {
5575
 *     // Success: use result.value() or *result
5576
 * } else {
5577
 *     // Error: handle result.error()
5578
 * }
5579
 * ```
5580
 */
5581
template <class result_type = ada::url_aggregator>
5582
using result = tl::expected<result_type, ada::errors>;
5583
5584
/**
5585
 * Parses a URL string according to the WHATWG URL Standard.
5586
 *
5587
 * This is the main entry point for URL parsing in Ada. The function takes
5588
 * a string input and optionally a base URL for resolving relative URLs.
5589
 *
5590
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5591
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5592
 *         memory-efficient as it stores components as offsets into a single
5593
 *         buffer.
5594
 *
5595
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5596
 *        Leading and trailing whitespace is automatically trimmed.
5597
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5598
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5599
 *
5600
 * @return A `result<result_type>` containing either the parsed URL on success,
5601
 *         or an error code on failure. Use the boolean conversion or
5602
 *         `has_value()` to check for success.
5603
 *
5604
 * @note The parser is fully compliant with the WHATWG URL Standard.
5605
 *
5606
 * Parsing fails if the input or the resulting normalized URL exceeds
5607
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5608
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5609
 * a short input that normalizes into a long URL is still rejected.
5610
 *
5611
 * @example
5612
 * ```cpp
5613
 * // Parse an absolute URL
5614
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5615
 * if (url) {
5616
 *     std::cout << url->get_hostname(); // "example.com"
5617
 *     std::cout << url->get_pathname(); // "/path"
5618
 * }
5619
 *
5620
 * // Parse a relative URL with a base
5621
 * auto base = ada::parse("https://example.com/dir/");
5622
 * if (base) {
5623
 *     auto relative = ada::parse("../other/page", &*base);
5624
 *     if (relative) {
5625
 *         std::cout << relative->get_href(); //
5626
 * "https://example.com/other/page"
5627
 *     }
5628
 * }
5629
 * ```
5630
 *
5631
 * @see https://url.spec.whatwg.org/#url-parsing
5632
 */
5633
template <class result_type = ada::url_aggregator>
5634
ada_warn_unused ada::result<result_type> parse(
5635
    std::string_view input, const result_type* base_url = nullptr);
5636
5637
extern template ada::result<url> parse<url>(std::string_view input,
5638
                                            const url* base_url);
5639
extern template ada::result<url_aggregator> parse<url_aggregator>(
5640
    std::string_view input, const url_aggregator* base_url);
5641
5642
/**
5643
 * Checks whether a URL string can be successfully parsed.
5644
 *
5645
 * Equivalent to `parse(input, base).has_value()` for every input, including
5646
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5647
 *
5648
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5649
 * @param base_input Optional base URL string for relative inputs.
5650
 * @return `true` if parsing would succeed, `false` otherwise.
5651
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5652
 */
5653
bool can_parse(std::string_view input,
5654
               const std::string_view* base_input = nullptr);
5655
5656
#if ADA_INCLUDE_URL_PATTERN
5657
/**
5658
 * Parses a URL pattern according to the URLPattern specification.
5659
 *
5660
 * URL patterns provide a syntax for matching URLs against patterns, similar
5661
 * to how regular expressions match strings. This is useful for routing and
5662
 * URL-based dispatching.
5663
 *
5664
 * @tparam regex_provider The regex implementation to use for pattern matching.
5665
 *
5666
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5667
 *        struct specifying individual component patterns.
5668
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5669
 *        resolving relative patterns.
5670
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5671
 *
5672
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5673
 *         or an error code on failure.
5674
 *
5675
 * @see https://urlpattern.spec.whatwg.org
5676
 */
5677
template <url_pattern_regex::regex_concept regex_provider>
5678
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5679
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5680
                  const std::string_view* base_url = nullptr,
5681
                  const url_pattern_options* options = nullptr);
5682
5683
template <url_pattern_regex::regex_concept regex_provider>
5684
class url_pattern_list;
5685
5686
/**
5687
 * Compiles a set of URLPattern pathname patterns into an ada::url_pattern_list
5688
 * (experimental): one structure that answers "which route matches this
5689
 * pathname, and what are its group values?" without a loop over the
5690
 * patterns and without executing regular expressions for routes written in
5691
 * the static / ":param" / "*" subset.
5692
 *
5693
 * @tparam regex_provider The regex implementation, as for parse_url_pattern;
5694
 *         it is used only for routes that need URLPattern regexp semantics.
5695
 *
5696
 * @param pathname_patterns URLPattern pathname patterns (valid UTF-8), for
5697
 *        example "/users/:id". The index of a pattern is the route index
5698
 *        reported by url_pattern_list::match; duplicates are allowed.
5699
 * @param base_url Optional pointer to a base URL string (valid UTF-8): each
5700
 *        pattern is then processed as the pathname of a URLPatternInit with
5701
 *        that base URL (relative patterns resolve against its path).
5702
 * @param options Optional pointer to configuration options (ignore_case),
5703
 *        applied to every route as parse_url_pattern applies them.
5704
 *
5705
 * @return A `tl::expected` containing either the compiled list on success,
5706
 *         or an error code when a pattern is not a valid URLPattern pathname
5707
 *         pattern (errors::type_error, as the URLPattern constructor).
5708
 *
5709
 * @see https://github.com/whatwg/urlpattern/issues/166
5710
 */
5711
template <url_pattern_regex::regex_concept regex_provider>
5712
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5713
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
5714
                       const std::string_view* base_url = nullptr,
5715
                       const url_pattern_options* options = nullptr);
5716
5717
/**
5718
 * Compiles the pathname components of existing url_pattern objects into an
5719
 * ada::url_pattern_list (experimental). Only the pathname component of each
5720
 * pattern is used; routes that need the regex provider reuse the pattern's
5721
 * already compiled pathname component. All patterns must share the same
5722
 * ignore_case setting (errors::type_error otherwise); the pathname is
5723
 * compiled as for a special-scheme URL.
5724
 */
5725
template <url_pattern_regex::regex_concept regex_provider>
5726
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5727
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
5728
#endif  // ADA_INCLUDE_URL_PATTERN
5729
5730
/**
5731
 * Converts a file system path to a file:// URL.
5732
 *
5733
 * Creates a properly formatted file URL from a local file system path.
5734
 * Handles platform-specific path separators and percent-encoding.
5735
 *
5736
 * Respects `get_max_input_length()`: if the input path or the resulting
5737
 * `file://` href would exceed the limit, returns an empty string.
5738
 *
5739
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5740
 *
5741
 * @return A file:// URL string representing the given path, or empty on
5742
 *         length-limit rejection.
5743
 */
5744
std::string href_from_file(std::string_view path);
5745
5746
/**
5747
 * Sets the maximum allowed length for URLs.
5748
 *
5749
 * Both the raw input and the resulting normalized URL (the href) are checked
5750
 * against this limit. Parsing or setter calls that would produce a URL
5751
 * exceeding this length are rejected. The same limit also applies to
5752
 * `href_from_file` and to query strings passed to `url_search_params`
5753
 * construction / `reset`. The value must fit in a uint32_t.
5754
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5755
 *
5756
 * @param length The new maximum URL length in bytes.
5757
 */
5758
void set_max_input_length(uint32_t length);
5759
5760
/**
5761
 * Returns the current maximum allowed length for URLs.
5762
 *
5763
 * @return The current maximum URL length in bytes.
5764
 */
5765
uint32_t get_max_input_length();
5766
5767
}  // namespace ada
5768
5769
#endif  // ADA_IMPLEMENTATION_H
5770
/* end file include/ada/implementation.h */
5771
5772
#include <cstdint>
5773
#include <optional>
5774
#include <ostream>
5775
#include <string>
5776
#include <string_view>
5777
#include <unordered_map>
5778
#include <utility>
5779
#include <variant>
5780
#include <vector>
5781
5782
#if ADA_TESTING
5783
#include <iostream>
5784
#endif  // ADA_TESTING
5785
5786
#if ADA_INCLUDE_URL_PATTERN
5787
namespace ada {
5788
5789
enum class url_pattern_part_type : uint8_t {
5790
  // The part represents a simple fixed text string.
5791
  FIXED_TEXT,
5792
  // The part represents a matching group with a custom regular expression.
5793
  REGEXP,
5794
  // The part represents a matching group that matches code points up to the
5795
  // next separator code point. This is typically used for a named group like
5796
  // ":foo" that does not have a custom regular expression.
5797
  SEGMENT_WILDCARD,
5798
  // The part represents a matching group that greedily matches all code points.
5799
  // This is typically used for the "*" wildcard matching group.
5800
  FULL_WILDCARD,
5801
};
5802
5803
// Pattern type for fast-path matching optimization.
5804
// This allows skipping expensive regex evaluation for common simple patterns.
5805
enum class url_pattern_component_type : uint8_t {
5806
  // Pattern is "^$" - only matches empty string
5807
  EMPTY,
5808
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5809
  EXACT_MATCH,
5810
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5811
  FULL_WILDCARD,
5812
  // Pattern requires actual regex evaluation
5813
  REGEXP,
5814
};
5815
5816
enum class url_pattern_part_modifier : uint8_t {
5817
  // The part does not have a modifier.
5818
  none,
5819
  // The part has an optional modifier indicated by the U+003F (?) code point.
5820
  optional,
5821
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5822
  // point.
5823
  zero_or_more,
5824
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5825
  // point.
5826
  one_or_more,
5827
};
5828
5829
// @see https://urlpattern.spec.whatwg.org/#part
5830
class url_pattern_part {
5831
 public:
5832
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5833
                   url_pattern_part_modifier _modifier)
5834
0
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5835
5836
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5837
                   url_pattern_part_modifier _modifier, std::string&& _name,
5838
                   std::string&& _prefix, std::string&& _suffix)
5839
0
      : type(_type),
5840
0
        value(std::move(_value)),
5841
0
        modifier(_modifier),
5842
0
        name(std::move(_name)),
5843
0
        prefix(std::move(_prefix)),
5844
0
        suffix(std::move(_suffix)) {}
5845
  // A part has an associated type, a string, which must be set upon creation.
5846
  url_pattern_part_type type;
5847
  // A part has an associated value, a string, which must be set upon creation.
5848
  std::string value;
5849
  // A part has an associated modifier a string, which must be set upon
5850
  // creation.
5851
  url_pattern_part_modifier modifier;
5852
  // A part has an associated name, a string, initially the empty string.
5853
  std::string name{};
5854
  // A part has an associated prefix, a string, initially the empty string.
5855
  std::string prefix{};
5856
  // A part has an associated suffix, a string, initially the empty string.
5857
  std::string suffix{};
5858
5859
  inline bool is_regexp() const noexcept;
5860
};
5861
5862
// @see https://urlpattern.spec.whatwg.org/#options-header
5863
struct url_pattern_compile_component_options {
5864
  url_pattern_compile_component_options() = default;
5865
  explicit url_pattern_compile_component_options(
5866
      std::optional<char> new_delimiter = std::nullopt,
5867
      std::optional<char> new_prefix = std::nullopt) noexcept
5868
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5869
5870
  inline std::string_view get_delimiter() const ada_warn_unused;
5871
  inline std::string_view get_prefix() const ada_warn_unused;
5872
5873
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5874
  bool ignore_case = false;
5875
5876
  static url_pattern_compile_component_options DEFAULT;
5877
  static url_pattern_compile_component_options HOSTNAME;
5878
  static url_pattern_compile_component_options PATHNAME;
5879
5880
 private:
5881
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5882
  std::optional<char> delimiter{};
5883
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5884
  std::optional<char> prefix{};
5885
};
5886
5887
// The default options is an options struct with delimiter code point set to
5888
// the empty string and prefix code point set to the empty string.
5889
inline url_pattern_compile_component_options
5890
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5891
5892
// The hostname options is an options struct with delimiter code point set
5893
// "." and prefix code point set to the empty string.
5894
inline url_pattern_compile_component_options
5895
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5896
5897
// The pathname options is an options struct with delimiter code point set
5898
// "/" and prefix code point set to "/".
5899
inline url_pattern_compile_component_options
5900
    url_pattern_compile_component_options::PATHNAME('/', '/');
5901
5902
// A struct providing the URLPattern matching results for a single
5903
// URL component. The URLPatternComponentResult is only ever used
5904
// as a member attribute of a URLPatternResult struct. The
5905
// URLPatternComponentResult API is defined as part of the URLPattern
5906
// specification.
5907
struct url_pattern_component_result {
5908
  std::string input;
5909
  std::unordered_map<std::string, std::optional<std::string>> groups;
5910
5911
  bool operator==(const url_pattern_component_result&) const;
5912
5913
#if ADA_TESTING
5914
  friend void PrintTo(const url_pattern_component_result& result,
5915
                      std::ostream* os) {
5916
    *os << "input: '" << result.input << "', group: ";
5917
    for (const auto& group : result.groups) {
5918
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5919
          << ") ";
5920
    }
5921
  }
5922
#endif  // ADA_TESTING
5923
};
5924
5925
template <url_pattern_regex::regex_concept regex_provider>
5926
class url_pattern_component {
5927
 public:
5928
  url_pattern_component() = default;
5929
5930
  // This function explicitly takes a std::string because it is moved.
5931
  // To avoid unnecessary copy, move each value while calling the constructor.
5932
  url_pattern_component(std::string&& new_pattern,
5933
                        typename regex_provider::regex_type&& new_regexp,
5934
                        std::vector<std::string>&& new_group_name_list,
5935
                        bool new_has_regexp_groups,
5936
                        url_pattern_component_type new_type,
5937
                        std::string&& new_exact_match_value = {})
5938
      : regexp(std::move(new_regexp)),
5939
        pattern(std::move(new_pattern)),
5940
        group_name_list(std::move(new_group_name_list)),
5941
        exact_match_value(std::move(new_exact_match_value)),
5942
        has_regexp_groups(new_has_regexp_groups),
5943
        type(new_type) {}
5944
5945
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5946
  template <url_pattern_encoding_callback F>
5947
  static tl::expected<url_pattern_component, errors> compile(
5948
      std::string_view input, F& encoding_callback,
5949
      url_pattern_compile_component_options& options);
5950
5951
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5952
  url_pattern_component_result create_component_match_result(
5953
      std::string&& input,
5954
      std::vector<std::optional<std::string>>&& exec_result);
5955
5956
  // Fast path test that returns true/false without constructing result groups.
5957
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5958
  // Note: Not marked noexcept because the regex provider may throw.
5959
  bool fast_test(std::string_view input) const;
5960
5961
  // Fast path match that returns capture groups without regex for simple
5962
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5963
  // capture groups.
5964
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5965
      std::string_view input) const;
5966
5967
#if ADA_TESTING
5968
  friend void PrintTo(const url_pattern_component& component,
5969
                      std::ostream* os) {
5970
    *os << "pattern: '" << component.pattern
5971
        << "', has_regexp_groups: " << component.has_regexp_groups
5972
        << "group_name_list: ";
5973
    for (const auto& name : component.group_name_list) {
5974
      *os << name << ", ";
5975
    }
5976
  }
5977
#endif  // ADA_TESTING
5978
5979
  typename regex_provider::regex_type regexp{};
5980
  std::string pattern{};
5981
  std::vector<std::string> group_name_list{};
5982
  // For EXACT_MATCH type: the literal string to compare against
5983
  std::string exact_match_value{};
5984
  bool has_regexp_groups = false;
5985
  // Cached pattern type for fast-path optimization
5986
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5987
};
5988
5989
// A URLPattern input can be either a string or a URLPatternInit object.
5990
// If it is a string, it must be a valid UTF-8 string.
5991
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5992
5993
// A struct providing the URLPattern matching results for all
5994
// components of a URL. The URLPatternResult API is defined as
5995
// part of the URLPattern specification.
5996
struct url_pattern_result {
5997
  std::vector<url_pattern_input> inputs;
5998
  url_pattern_component_result protocol;
5999
  url_pattern_component_result username;
6000
  url_pattern_component_result password;
6001
  url_pattern_component_result hostname;
6002
  url_pattern_component_result port;
6003
  url_pattern_component_result pathname;
6004
  url_pattern_component_result search;
6005
  url_pattern_component_result hash;
6006
};
6007
6008
struct url_pattern_options {
6009
  bool ignore_case = false;
6010
6011
#if ADA_TESTING
6012
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
6013
    *os << "ignore_case: '" << options.ignore_case;
6014
  }
6015
#endif  // ADA_TESTING
6016
};
6017
6018
/**
6019
 * @brief URL pattern matching class implementing the URLPattern API.
6020
 *
6021
 * URLPattern provides a way to match URLs against patterns with wildcards
6022
 * and named capture groups. It's useful for routing, URL-based dispatching,
6023
 * and URL validation.
6024
 *
6025
 * Pattern syntax supports:
6026
 * - Literal text matching
6027
 * - Named groups: `:name` (matches up to the next separator)
6028
 * - Wildcards: `*` (matches everything)
6029
 * - Custom regex: `(pattern)`
6030
 * - Optional segments: `:name?`
6031
 * - Repeated segments: `:name+`, `:name*`
6032
 *
6033
 * @tparam regex_provider The regex implementation to use for pattern matching.
6034
 *         Must satisfy the url_pattern_regex::regex_concept.
6035
 *
6036
 * @note All string inputs must be valid UTF-8.
6037
 *
6038
 * @see https://urlpattern.spec.whatwg.org/
6039
 */
6040
template <url_pattern_regex::regex_concept regex_provider>
6041
class url_pattern {
6042
 public:
6043
  url_pattern() = default;
6044
6045
  /**
6046
   * If non-null, base_url must pointer at a valid UTF-8 string.
6047
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
6048
   */
6049
  result<std::optional<url_pattern_result>> exec(
6050
      const url_pattern_input& input,
6051
      const std::string_view* base_url = nullptr);
6052
6053
  /**
6054
   * If non-null, base_url must pointer at a valid UTF-8 string.
6055
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
6056
   */
6057
  result<bool> test(const url_pattern_input& input,
6058
                    const std::string_view* base_url = nullptr);
6059
6060
  /**
6061
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
6062
   * This function expects a valid UTF-8 string if input is a string.
6063
   */
6064
  result<std::optional<url_pattern_result>> match(
6065
      const url_pattern_input& input,
6066
      const std::string_view* base_url_string = nullptr);
6067
6068
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
6069
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
6070
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
6071
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
6072
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
6073
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
6074
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
6075
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
6076
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
6077
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
6078
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
6079
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
6080
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
6081
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
6082
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
6083
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
6084
6085
  // If ignoreCase is true, the JavaScript regular expression created for each
6086
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
6087
  [[nodiscard]] bool ignore_case() const;
6088
6089
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
6090
  [[nodiscard]] bool has_regexp_groups() const;
6091
6092
  // Helper to test all components at once. Returns true if all match.
6093
  [[nodiscard]] bool test_components(
6094
      std::string_view protocol, std::string_view username,
6095
      std::string_view password, std::string_view hostname,
6096
      std::string_view port, std::string_view pathname, std::string_view search,
6097
      std::string_view hash) const;
6098
6099
#if ADA_TESTING
6100
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
6101
    *os << "protocol_component: '" << c.get_protocol() << ", ";
6102
    *os << "username_component: '" << c.get_username() << ", ";
6103
    *os << "password_component: '" << c.get_password() << ", ";
6104
    *os << "hostname_component: '" << c.get_hostname() << ", ";
6105
    *os << "port_component: '" << c.get_port() << ", ";
6106
    *os << "pathname_component: '" << c.get_pathname() << ", ";
6107
    *os << "search_component: '" << c.get_search() << ", ";
6108
    *os << "hash_component: '" << c.get_hash();
6109
  }
6110
#endif  // ADA_TESTING
6111
6112
  template <url_pattern_regex::regex_concept P>
6113
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
6114
      std::variant<std::string_view, url_pattern_init>&& input,
6115
      const std::string_view* base_url, const url_pattern_options* options);
6116
6117
  /**
6118
   * @private
6119
   * We can not make this private due to a LLVM bug.
6120
   * Ref: https://github.com/ada-url/ada/pull/859
6121
   */
6122
  url_pattern_component<regex_provider> protocol_component{};
6123
  /**
6124
   * @private
6125
   * We can not make this private due to a LLVM bug.
6126
   * Ref: https://github.com/ada-url/ada/pull/859
6127
   */
6128
  url_pattern_component<regex_provider> username_component{};
6129
  /**
6130
   * @private
6131
   * We can not make this private due to a LLVM bug.
6132
   * Ref: https://github.com/ada-url/ada/pull/859
6133
   */
6134
  url_pattern_component<regex_provider> password_component{};
6135
  /**
6136
   * @private
6137
   * We can not make this private due to a LLVM bug.
6138
   * Ref: https://github.com/ada-url/ada/pull/859
6139
   */
6140
  url_pattern_component<regex_provider> hostname_component{};
6141
  /**
6142
   * @private
6143
   * We can not make this private due to a LLVM bug.
6144
   * Ref: https://github.com/ada-url/ada/pull/859
6145
   */
6146
  url_pattern_component<regex_provider> port_component{};
6147
  /**
6148
   * @private
6149
   * We can not make this private due to a LLVM bug.
6150
   * Ref: https://github.com/ada-url/ada/pull/859
6151
   */
6152
  url_pattern_component<regex_provider> pathname_component{};
6153
  /**
6154
   * @private
6155
   * We can not make this private due to a LLVM bug.
6156
   * Ref: https://github.com/ada-url/ada/pull/859
6157
   */
6158
  url_pattern_component<regex_provider> search_component{};
6159
  /**
6160
   * @private
6161
   * We can not make this private due to a LLVM bug.
6162
   * Ref: https://github.com/ada-url/ada/pull/859
6163
   */
6164
  url_pattern_component<regex_provider> hash_component{};
6165
  /**
6166
   * @private
6167
   * We can not make this private due to a LLVM bug.
6168
   * Ref: https://github.com/ada-url/ada/pull/859
6169
   */
6170
  bool ignore_case_ = false;
6171
};
6172
}  // namespace ada
6173
#endif  // ADA_INCLUDE_URL_PATTERN
6174
#endif
6175
/* end file include/ada/url_pattern.h */
6176
/* begin file include/ada/url_pattern_helpers.h */
6177
/**
6178
 * @file url_pattern_helpers.h
6179
 * @brief Declaration for the URLPattern helpers.
6180
 */
6181
#ifndef ADA_URL_PATTERN_HELPERS_H
6182
#define ADA_URL_PATTERN_HELPERS_H
6183
6184
6185
#include <cstddef>
6186
#include <cstdint>
6187
#include <optional>
6188
#include <string>
6189
#include <string_view>
6190
#include <tuple>
6191
#include <unordered_set>
6192
#include <utility>
6193
#include <vector>
6194
6195
#if ADA_INCLUDE_URL_PATTERN
6196
namespace ada {
6197
enum class errors : uint8_t;
6198
}
6199
6200
namespace ada::url_pattern_helpers {
6201
6202
// @see https://urlpattern.spec.whatwg.org/#token
6203
enum class token_type : uint8_t {
6204
  INVALID_CHAR,    // 0
6205
  OPEN,            // 1
6206
  CLOSE,           // 2
6207
  REGEXP,          // 3
6208
  NAME,            // 4
6209
  CHAR,            // 5
6210
  ESCAPED_CHAR,    // 6
6211
  OTHER_MODIFIER,  // 7
6212
  ASTERISK,        // 8
6213
  END,             // 9
6214
};
6215
6216
#ifdef ADA_TESTING
6217
std::string to_string(token_type type);
6218
#endif  // ADA_TESTING
6219
6220
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6221
enum class token_policy {
6222
  strict,
6223
  lenient,
6224
};
6225
6226
// @see https://urlpattern.spec.whatwg.org/#tokens
6227
class token {
6228
 public:
6229
  token(token_type _type, size_t _index, std::string_view _value)
6230
0
      : type(_type), index(_index), value(_value) {}
6231
6232
  // A token has an associated type, a string, initially "invalid-char".
6233
  token_type type = token_type::INVALID_CHAR;
6234
6235
  // A token has an associated index, a number, initially 0. It is the position
6236
  // of the first code point in the pattern string represented by the token.
6237
  size_t index = 0;
6238
6239
  // A token has an associated value, a string, initially the empty string. It
6240
  // contains the code points from the pattern string represented by the token.
6241
  std::string_view value{};
6242
};
6243
6244
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6245
template <url_pattern_encoding_callback F>
6246
class url_pattern_parser {
6247
 public:
6248
  url_pattern_parser(F& encoding_callback_,
6249
                     std::string_view segment_wildcard_regexp_)
6250
0
      : encoding_callback(encoding_callback_),
6251
0
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6252
6253
0
  bool can_continue() const { return index < tokens.size(); }
6254
6255
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6256
  token* try_consume_token(token_type type);
6257
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6258
  token* try_consume_modifier_token();
6259
  // @see
6260
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6261
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6262
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6263
  std::string consume_text();
6264
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6265
  bool consume_required_token(token_type type);
6266
  // @see
6267
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6268
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6269
      ada_warn_unused;
6270
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6271
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6272
                                 token* regexp_or_wildcard_token,
6273
                                 std::string_view suyffix,
6274
                                 token* modifier_token) ada_warn_unused;
6275
6276
  std::vector<token> tokens{};
6277
  F& encoding_callback;
6278
  std::string segment_wildcard_regexp;
6279
  std::vector<url_pattern_part> parts{};
6280
  std::string pending_fixed_value{};
6281
  size_t index = 0;
6282
  size_t next_numeric_name = 0;
6283
  // Names already taken by a part, for the "is a duplicate name" check in
6284
  // add_part. A linear scan of the part list there is O(n^2) over a pattern
6285
  // with n named parts (e.g. "/*" repeated); this set keeps it amortized O(1).
6286
  std::unordered_set<std::string> seen_names{};
6287
};
6288
6289
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6290
class Tokenizer {
6291
 public:
6292
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6293
0
      : input(new_input), policy(new_policy) {}
6294
6295
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6296
  constexpr void get_next_code_point();
6297
6298
  // True when the most recent decoded unit was malformed UTF-8.
6299
0
  bool had_invalid_code_point() const { return invalid_code_point; }
6300
6301
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6302
  constexpr void seek_and_get_next_code_point(size_t index);
6303
6304
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6305
6306
  void add_token(token_type type, size_t next_position, size_t value_position,
6307
                 size_t value_length);
6308
6309
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6310
  void add_token_with_default_length(token_type type, size_t next_position,
6311
                                     size_t value_position);
6312
6313
  // @see
6314
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6315
  void add_token_with_defaults(token_type type);
6316
6317
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6318
  std::optional<errors> process_tokenizing_error(
6319
      size_t next_position, size_t value_position) ada_warn_unused;
6320
6321
  friend tl::expected<std::vector<token>, errors> tokenize(
6322
      std::string_view input, token_policy policy);
6323
6324
 private:
6325
  // has an associated input, a pattern string, initially the empty string.
6326
  std::string_view input;
6327
  // has an associated policy, a tokenize policy, initially "strict".
6328
  token_policy policy;
6329
  // has an associated token list, a token list, initially an empty list.
6330
  std::vector<token> token_list{};
6331
  // has an associated index, a number, initially 0.
6332
  size_t index = 0;
6333
  // has an associated next index, a number, initially 0.
6334
  size_t next_index = 0;
6335
  // has an associated code point, a Unicode code point, initially null.
6336
  char32_t code_point{};
6337
  // Tracks whether the last decoded code point was malformed UTF-8.
6338
  bool invalid_code_point = false;
6339
};
6340
6341
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6342
template <url_pattern_regex::regex_concept regex_provider>
6343
struct constructor_string_parser {
6344
  explicit constructor_string_parser(std::string_view new_input,
6345
                                     std::vector<token>&& new_token_list)
6346
      : input(new_input), token_list(std::move(new_token_list)) {}
6347
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6348
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6349
6350
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6351
  enum class State {
6352
    INIT,
6353
    PROTOCOL,
6354
    AUTHORITY,
6355
    USERNAME,
6356
    PASSWORD,
6357
    HOSTNAME,
6358
    PORT,
6359
    PATHNAME,
6360
    SEARCH,
6361
    HASH,
6362
    DONE,
6363
  };
6364
6365
  // @see
6366
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6367
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6368
6369
 private:
6370
  // @see https://urlpattern.spec.whatwg.org/#rewind
6371
  constexpr void rewind();
6372
6373
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6374
  constexpr bool is_hash_prefix();
6375
6376
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6377
  constexpr bool is_search_prefix();
6378
6379
  // @see https://urlpattern.spec.whatwg.org/#change-state
6380
  void change_state(State state, size_t skip);
6381
6382
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6383
  constexpr bool is_group_open() const;
6384
6385
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6386
  constexpr bool is_group_close() const;
6387
6388
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6389
  constexpr bool is_protocol_suffix() const;
6390
6391
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6392
  constexpr bool next_is_authority_slashes() const;
6393
6394
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6395
  constexpr bool is_an_identity_terminator() const;
6396
6397
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6398
  constexpr bool is_pathname_start() const;
6399
6400
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6401
  constexpr bool is_password_prefix() const;
6402
6403
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6404
  constexpr bool is_an_ipv6_open() const;
6405
6406
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6407
  constexpr bool is_an_ipv6_close() const;
6408
6409
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6410
  constexpr bool is_port_prefix() const;
6411
6412
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6413
  constexpr bool is_non_special_pattern_char(size_t index,
6414
                                             uint32_t value) const;
6415
6416
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6417
  constexpr const token* get_safe_token(size_t index) const;
6418
6419
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6420
  std::string make_component_string();
6421
  // has an associated input, a string, which must be set upon creation.
6422
  std::string_view input;
6423
  // has an associated token list, a token list, which must be set upon
6424
  // creation.
6425
  std::vector<token> token_list;
6426
  // has an associated result, a URLPatternInit, initially set to a new
6427
  // URLPatternInit.
6428
  url_pattern_init result{};
6429
  // has an associated component start, a number, initially set to 0.
6430
  size_t component_start = 0;
6431
  // has an associated token index, a number, initially set to 0.
6432
  size_t token_index = 0;
6433
  // has an associated token increment, a number, initially set to 1.
6434
  size_t token_increment = 1;
6435
  // has an associated group depth, a number, initially set to 0.
6436
  size_t group_depth = 0;
6437
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6438
  // 0.
6439
  size_t hostname_ipv6_bracket_depth = 0;
6440
  // has an associated protocol matches a special scheme flag, a boolean,
6441
  // initially set to false.
6442
  bool protocol_matches_a_special_scheme_flag = false;
6443
  // has an associated state, a string, initially set to "init".
6444
  State state = State::INIT;
6445
};
6446
6447
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6448
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6449
6450
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6451
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6452
6453
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6454
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6455
6456
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6457
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6458
6459
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6460
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6461
    std::string_view input);
6462
6463
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6464
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6465
6466
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6467
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6468
    std::string_view input, std::string_view protocol);
6469
6470
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6471
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6472
6473
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6474
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6475
    std::string_view input);
6476
6477
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6478
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6479
6480
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6481
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6482
6483
// @see https://urlpattern.spec.whatwg.org/#tokenize
6484
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6485
                                                  token_policy policy);
6486
6487
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6488
std::string process_base_url_string(std::string_view input,
6489
                                    url_pattern_init::process_type type);
6490
6491
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6492
std::string escape_pattern_string(std::string_view input);
6493
6494
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6495
std::string escape_regexp_string(std::string_view input);
6496
6497
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6498
constexpr bool is_absolute_pathname(
6499
    std::string_view input, url_pattern_init::process_type type) noexcept;
6500
6501
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6502
template <url_pattern_encoding_callback F>
6503
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6504
    std::string_view input, url_pattern_compile_component_options& options,
6505
    F& encoding_callback);
6506
6507
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6508
std::string generate_pattern_string(
6509
    std::vector<url_pattern_part>& part_list,
6510
    url_pattern_compile_component_options& options);
6511
6512
// @see
6513
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6514
std::tuple<std::string, std::vector<std::string>>
6515
generate_regular_expression_and_name_list(
6516
    const std::vector<url_pattern_part>& part_list,
6517
    url_pattern_compile_component_options options);
6518
6519
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6520
bool is_ipv6_address(std::string_view input) noexcept;
6521
6522
// @see
6523
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6524
template <url_pattern_regex::regex_concept regex_provider>
6525
bool protocol_component_matches_special_scheme(
6526
    ada::url_pattern_component<regex_provider>& input);
6527
6528
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6529
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6530
6531
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6532
std::string generate_segment_wildcard_regexp(
6533
    url_pattern_compile_component_options options);
6534
6535
}  // namespace ada::url_pattern_helpers
6536
#endif  // ADA_INCLUDE_URL_PATTERN
6537
#endif
6538
/* end file include/ada/url_pattern_helpers.h */
6539
6540
#include <cstdint>
6541
#include <string>
6542
#include <string_view>
6543
#include <utility>
6544
#include <variant>
6545
6546
namespace ada::parser {
6547
#if ADA_INCLUDE_URL_PATTERN
6548
template <url_pattern_regex::regex_concept regex_provider>
6549
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6550
    std::variant<std::string_view, url_pattern_init>&& input,
6551
    const std::string_view* base_url, const url_pattern_options* options) {
6552
  // Let init be null.
6553
  url_pattern_init init;
6554
6555
  // If input is a scalar value string then:
6556
  if (std::holds_alternative<std::string_view>(input)) {
6557
    // Set init to the result of running parse a constructor string given input.
6558
    auto parse_result =
6559
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6560
            std::get<std::string_view>(input));
6561
    if (!parse_result) {
6562
      ada_log("constructor_string_parser::parse failed");
6563
      return tl::unexpected(parse_result.error());
6564
    }
6565
    init = std::move(*parse_result);
6566
    // If baseURL is null and init["protocol"] does not exist, then throw a
6567
    // TypeError.
6568
    if (!base_url && !init.protocol) {
6569
      ada_log("base url is null and protocol is not set");
6570
      return tl::unexpected(errors::type_error);
6571
    }
6572
6573
    // If baseURL is not null, set init["baseURL"] to baseURL.
6574
    if (base_url) {
6575
      init.base_url = std::string(*base_url);
6576
    }
6577
  } else {
6578
    // Assert: input is a URLPatternInit.
6579
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6580
    // If baseURL is not null, then throw a TypeError.
6581
    if (base_url) {
6582
      ada_log("base url is not null");
6583
      return tl::unexpected(errors::type_error);
6584
    }
6585
    // Optimization: Avoid copy by moving the input value.
6586
    // Set init to input.
6587
    init = std::move(std::get<url_pattern_init>(input));
6588
  }
6589
6590
  // Let processedInit be the result of process a URLPatternInit given init,
6591
  // "pattern", null, null, null, null, null, null, null, and null.
6592
  auto processed_init =
6593
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6594
  if (!processed_init) {
6595
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6596
    return tl::unexpected(processed_init.error());
6597
  }
6598
6599
  // For each componentName of  "protocol", "username", "password", "hostname",
6600
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6601
  // not exist, then set processedInit[componentName] to "*".
6602
  ADA_ASSERT_TRUE(processed_init.has_value());
6603
  if (!processed_init->protocol) processed_init->protocol = "*";
6604
  if (!processed_init->username) processed_init->username = "*";
6605
  if (!processed_init->password) processed_init->password = "*";
6606
  if (!processed_init->hostname) processed_init->hostname = "*";
6607
  if (!processed_init->port) processed_init->port = "*";
6608
  if (!processed_init->pathname) processed_init->pathname = "*";
6609
  if (!processed_init->search) processed_init->search = "*";
6610
  if (!processed_init->hash) processed_init->hash = "*";
6611
6612
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6613
  ada_log("-- processed_init->username: ", processed_init->username.value());
6614
  ada_log("-- processed_init->password: ", processed_init->password.value());
6615
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6616
  ada_log("-- processed_init->port: ", processed_init->port.value());
6617
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6618
  ada_log("-- processed_init->search: ", processed_init->search.value());
6619
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6620
6621
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6622
  // is a string which represents its corresponding default port in radix-10
6623
  // using ASCII digits then set processedInit["port"] to the empty string.
6624
  // TODO: Optimization opportunity.
6625
  if (scheme::is_special(*processed_init->protocol)) {
6626
    // file is the only special scheme with no default port; get_special_port
6627
    // returns 0 as its sentinel, so a literal "0" port must not be mistaken for
6628
    // a default and dropped.
6629
    uint16_t default_port = scheme::get_special_port(*processed_init->protocol);
6630
    if (default_port != 0 &&
6631
        std::to_string(default_port) == processed_init->port.value()) {
6632
      processed_init->port->clear();
6633
    }
6634
  }
6635
6636
  // Let urlPattern be a new URL pattern.
6637
  url_pattern<regex_provider> url_pattern_{};
6638
6639
  // Set urlPattern's protocol component to the result of compiling a component
6640
  // given processedInit["protocol"], canonicalize a protocol, and default
6641
  // options.
6642
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6643
      processed_init->protocol.value(),
6644
      url_pattern_helpers::canonicalize_protocol,
6645
      url_pattern_compile_component_options::DEFAULT);
6646
  if (!protocol_component) {
6647
    ada_log("url_pattern_component::compile failed for protocol ",
6648
            processed_init->protocol.value());
6649
    return tl::unexpected(protocol_component.error());
6650
  }
6651
  url_pattern_.protocol_component = std::move(*protocol_component);
6652
6653
  // Set urlPattern's username component to the result of compiling a component
6654
  // given processedInit["username"], canonicalize a username, and default
6655
  // options.
6656
  auto username_component = url_pattern_component<regex_provider>::compile(
6657
      processed_init->username.value(),
6658
      url_pattern_helpers::canonicalize_username,
6659
      url_pattern_compile_component_options::DEFAULT);
6660
  if (!username_component) {
6661
    ada_log("url_pattern_component::compile failed for username ",
6662
            processed_init->username.value());
6663
    return tl::unexpected(username_component.error());
6664
  }
6665
  url_pattern_.username_component = std::move(*username_component);
6666
6667
  // Set urlPattern's password component to the result of compiling a component
6668
  // given processedInit["password"], canonicalize a password, and default
6669
  // options.
6670
  auto password_component = url_pattern_component<regex_provider>::compile(
6671
      processed_init->password.value(),
6672
      url_pattern_helpers::canonicalize_password,
6673
      url_pattern_compile_component_options::DEFAULT);
6674
  if (!password_component) {
6675
    ada_log("url_pattern_component::compile failed for password ",
6676
            processed_init->password.value());
6677
    return tl::unexpected(password_component.error());
6678
  }
6679
  url_pattern_.password_component = std::move(*password_component);
6680
6681
  // TODO: Optimization opportunity. The following if statement can be
6682
  // simplified.
6683
  // If the result running hostname pattern is an IPv6 address given
6684
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6685
  // to the result of compiling a component given processedInit["hostname"],
6686
  // canonicalize an IPv6 hostname, and hostname options.
6687
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6688
    ada_log("processed_init->hostname is ipv6 address");
6689
    // then set urlPattern's hostname component to the result of compiling a
6690
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6691
    // and hostname options.
6692
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6693
        processed_init->hostname.value(),
6694
        url_pattern_helpers::canonicalize_ipv6_hostname,
6695
        url_pattern_compile_component_options::DEFAULT);
6696
    if (!hostname_component) {
6697
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6698
              processed_init->hostname.value());
6699
      return tl::unexpected(hostname_component.error());
6700
    }
6701
    url_pattern_.hostname_component = std::move(*hostname_component);
6702
  } else {
6703
    // Otherwise, set urlPattern's hostname component to the result of compiling
6704
    // a component given processedInit["hostname"], canonicalize a hostname, and
6705
    // hostname options.
6706
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6707
        processed_init->hostname.value(),
6708
        url_pattern_helpers::canonicalize_hostname,
6709
        url_pattern_compile_component_options::HOSTNAME);
6710
    if (!hostname_component) {
6711
      ada_log("url_pattern_component::compile failed for hostname ",
6712
              processed_init->hostname.value());
6713
      return tl::unexpected(hostname_component.error());
6714
    }
6715
    url_pattern_.hostname_component = std::move(*hostname_component);
6716
  }
6717
6718
  // Set urlPattern's port component to the result of compiling a component
6719
  // given processedInit["port"], canonicalize a port, and default options.
6720
  auto port_component = url_pattern_component<regex_provider>::compile(
6721
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6722
      url_pattern_compile_component_options::DEFAULT);
6723
  if (!port_component) {
6724
    ada_log("url_pattern_component::compile failed for port ",
6725
            processed_init->port.value());
6726
    return tl::unexpected(port_component.error());
6727
  }
6728
  url_pattern_.port_component = std::move(*port_component);
6729
6730
  // Let compileOptions be a copy of the default options with the ignore case
6731
  // property set to options["ignoreCase"].
6732
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6733
  if (options) {
6734
    compile_options.ignore_case = options->ignore_case;
6735
    // Recorded on the pattern so that url_pattern::ignore_case() reports the
6736
    // option it was created with.
6737
    url_pattern_.ignore_case_ = options->ignore_case;
6738
  }
6739
6740
  // TODO: Optimization opportunity: Simplify this if statement.
6741
  // If the result of running protocol component matches a special scheme given
6742
  // urlPattern's protocol component is true, then:
6743
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6744
          regex_provider>(url_pattern_.protocol_component)) {
6745
    // Let pathCompileOptions be copy of the pathname options with the ignore
6746
    // case property set to options["ignoreCase"].
6747
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6748
    if (options) {
6749
      path_compile_options.ignore_case = options->ignore_case;
6750
    }
6751
6752
    // Set urlPattern's pathname component to the result of compiling a
6753
    // component given processedInit["pathname"], canonicalize a pathname, and
6754
    // pathCompileOptions.
6755
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6756
        processed_init->pathname.value(),
6757
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6758
    if (!pathname_component) {
6759
      ada_log("url_pattern_component::compile failed for pathname ",
6760
              processed_init->pathname.value());
6761
      return tl::unexpected(pathname_component.error());
6762
    }
6763
    url_pattern_.pathname_component = std::move(*pathname_component);
6764
  } else {
6765
    // Otherwise set urlPattern's pathname component to the result of compiling
6766
    // a component given processedInit["pathname"], canonicalize an opaque
6767
    // pathname, and compileOptions.
6768
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6769
        processed_init->pathname.value(),
6770
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6771
    if (!pathname_component) {
6772
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6773
              processed_init->pathname.value());
6774
      return tl::unexpected(pathname_component.error());
6775
    }
6776
    url_pattern_.pathname_component = std::move(*pathname_component);
6777
  }
6778
6779
  // Set urlPattern's search component to the result of compiling a component
6780
  // given processedInit["search"], canonicalize a search, and compileOptions.
6781
  auto search_component = url_pattern_component<regex_provider>::compile(
6782
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6783
      compile_options);
6784
  if (!search_component) {
6785
    ada_log("url_pattern_component::compile failed for search ",
6786
            processed_init->search.value());
6787
    return tl::unexpected(search_component.error());
6788
  }
6789
  url_pattern_.search_component = std::move(*search_component);
6790
6791
  // Set urlPattern's hash component to the result of compiling a component
6792
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6793
  auto hash_component = url_pattern_component<regex_provider>::compile(
6794
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6795
      compile_options);
6796
  if (!hash_component) {
6797
    ada_log("url_pattern_component::compile failed for hash ",
6798
            processed_init->hash.value());
6799
    return tl::unexpected(hash_component.error());
6800
  }
6801
  url_pattern_.hash_component = std::move(*hash_component);
6802
6803
  // Return urlPattern.
6804
  return url_pattern_;
6805
}
6806
#endif  // ADA_INCLUDE_URL_PATTERN
6807
6808
}  // namespace ada::parser
6809
6810
#endif  // ADA_PARSER_INL_H
6811
/* end file include/ada/parser-inl.h */
6812
/* begin file include/ada/scheme-inl.h */
6813
/**
6814
 * @file scheme-inl.h
6815
 * @brief Definitions for the URL scheme.
6816
 */
6817
#ifndef ADA_SCHEME_INL_H
6818
#define ADA_SCHEME_INL_H
6819
6820
6821
#include <cstddef>
6822
#include <cstdint>
6823
#include <string_view>
6824
6825
namespace ada::scheme {
6826
6827
/**
6828
 * @namespace ada::scheme::details
6829
 * @brief Includes the definitions for scheme specific entities
6830
 */
6831
namespace details {
6832
// for use with is_special and get_special_port
6833
// Spaces, if present, are removed from URL.
6834
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6835
                                                "ftp",  "wss", "file",  " "};
6836
// for use with get_special_port
6837
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6838
6839
// @private
6840
// convert a string_view to a 64-bit integer key for fast comparison
6841
0
constexpr uint64_t make_key(std::string_view sv) {
6842
0
  uint64_t val = 0;
6843
0
  for (size_t i = 0; i < sv.size(); i++)
6844
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6845
0
  return val;
6846
0
}
6847
// precomputed keys for the special schemes, indexed by a hash of the input
6848
// string
6849
constexpr uint64_t scheme_keys[] = {
6850
    make_key("http"),   // 0: HTTP
6851
    0,                  // 1: sentinel
6852
    make_key("https"),  // 2: HTTPS
6853
    make_key("ws"),     // 3: WS
6854
    make_key("ftp"),    // 4: FTP
6855
    make_key("wss"),    // 5: WSS
6856
    make_key("file"),   // 6: FILE
6857
    0,                  // 7: sentinel
6858
};
6859
6860
// @private
6861
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6862
// n < 5
6863
0
inline uint64_t branchless_load5(const char* p, size_t n) {
6864
0
  uint64_t input = (uint8_t)p[0];
6865
0
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6866
0
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6867
0
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6868
0
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6869
0
  return input;
6870
0
}
6871
}  // namespace details
6872
6873
/****
6874
 * @private
6875
 * In is_special, get_scheme_type, and get_special_port, we
6876
 * use a standard hashing technique to find the index of the scheme in
6877
 * the is_special_list. The hashing technique is based on the size of
6878
 * the scheme and the first character of the scheme. It ensures that we
6879
 * do at most one string comparison per call. If the protocol is
6880
 * predictible (e.g., it is always "http"), we can get a better average
6881
 * performance by using a simpler approach where we loop and compare
6882
 * scheme with all possible protocols starting with the most likely
6883
 * protocol. Doing multiple comparisons may have a poor worst case
6884
 * performance, however. In this instance, we choose a potentially
6885
 * slightly lower best-case performance for a better worst-case
6886
 * performance. We can revisit this choice at any time.
6887
 *
6888
 * Reference:
6889
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6890
 * More C++ gems 17 (2000).
6891
 *
6892
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6893
 *
6894
 * Reference: https://github.com/ada-url/ada/issues/617
6895
 ****/
6896
6897
0
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6898
0
  if (scheme.empty()) {
6899
0
    return false;
6900
0
  }
6901
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6902
0
  const std::string_view target = details::is_special_list[hash_value];
6903
0
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6904
0
}
6905
0
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6906
0
  if (scheme.empty()) {
6907
0
    return 0;
6908
0
  }
6909
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6910
0
  const std::string_view target = details::is_special_list[hash_value];
6911
0
  if (scheme.size() == target.size() &&
6912
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6913
0
          details::scheme_keys[hash_value]) {
6914
0
    return details::special_ports[hash_value];
6915
0
  } else {
6916
0
    return 0;
6917
0
  }
6918
0
}
6919
0
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6920
0
  return details::special_ports[int(type)];
6921
0
}
6922
0
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6923
0
  if (scheme.empty()) {
6924
0
    return ada::scheme::NOT_SPECIAL;
6925
0
  }
6926
0
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6927
0
  const std::string_view target = details::is_special_list[hash_value];
6928
0
  if (scheme.size() == target.size() &&
6929
0
      details::branchless_load5(scheme.data(), scheme.size()) ==
6930
0
          details::scheme_keys[hash_value]) {
6931
0
    return ada::scheme::type(hash_value);
6932
0
  } else {
6933
0
    return ada::scheme::NOT_SPECIAL;
6934
0
  }
6935
0
}
6936
6937
}  // namespace ada::scheme
6938
6939
#endif  // ADA_SCHEME_INL_H
6940
/* end file include/ada/scheme-inl.h */
6941
/* begin file include/ada/serializers.h */
6942
/**
6943
 * @file serializers.h
6944
 * @brief IP address serialization utilities.
6945
 *
6946
 * This header provides functions for converting IP addresses to their
6947
 * string representations according to the WHATWG URL Standard.
6948
 */
6949
#ifndef ADA_SERIALIZERS_H
6950
#define ADA_SERIALIZERS_H
6951
6952
6953
#include <array>
6954
#include <cstddef>
6955
#include <cstdint>
6956
#include <string>
6957
6958
/**
6959
 * @namespace ada::serializers
6960
 * @brief IP address serialization functions.
6961
 *
6962
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6963
 */
6964
namespace ada::serializers {
6965
6966
/**
6967
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6968
 * Used for :: compression in IPv6 serialization.
6969
 *
6970
 * @param address The 8 16-bit pieces of the IPv6 address.
6971
 * @param[out] compress Index of the start of the longest zero sequence.
6972
 * @param[out] compress_length Length of the longest zero sequence.
6973
 */
6974
void find_longest_sequence_of_ipv6_pieces(
6975
    const std::array<uint16_t, 8>& address, size_t& compress,
6976
    size_t& compress_length) noexcept;
6977
6978
/**
6979
 * Serializes an IPv6 address to its string representation.
6980
 *
6981
 * @param address The 8 16-bit pieces of the IPv6 address.
6982
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6983
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6984
 */
6985
std::string ipv6(const std::array<uint16_t, 8>& address);
6986
6987
/**
6988
 * Serializes an IPv4 address to its dotted-decimal string representation.
6989
 *
6990
 * @param address The 32-bit IPv4 address as an integer.
6991
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6992
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6993
 */
6994
std::string ipv4(uint64_t address);
6995
6996
}  // namespace ada::serializers
6997
6998
#endif  // ADA_SERIALIZERS_H
6999
/* end file include/ada/serializers.h */
7000
/* begin file include/ada/state.h */
7001
/**
7002
 * @file state.h
7003
 * @brief URL parser state machine states.
7004
 *
7005
 * Defines the states used by the URL parsing state machine as specified
7006
 * in the WHATWG URL Standard.
7007
 *
7008
 * @see https://url.spec.whatwg.org/#url-parsing
7009
 */
7010
#ifndef ADA_STATE_H
7011
#define ADA_STATE_H
7012
7013
7014
#include <string>
7015
7016
namespace ada {
7017
7018
/**
7019
 * @brief States in the URL parsing state machine.
7020
 *
7021
 * The URL parser processes input through a sequence of states, each handling
7022
 * a specific part of the URL syntax.
7023
 *
7024
 * @see https://url.spec.whatwg.org/#url-parsing
7025
 */
7026
enum class state {
7027
  /**
7028
   * @see https://url.spec.whatwg.org/#authority-state
7029
   */
7030
  AUTHORITY,
7031
7032
  /**
7033
   * @see https://url.spec.whatwg.org/#scheme-start-state
7034
   */
7035
  SCHEME_START,
7036
7037
  /**
7038
   * @see https://url.spec.whatwg.org/#scheme-state
7039
   */
7040
  SCHEME,
7041
7042
  /**
7043
   * @see https://url.spec.whatwg.org/#host-state
7044
   */
7045
  HOST,
7046
7047
  /**
7048
   * @see https://url.spec.whatwg.org/#no-scheme-state
7049
   */
7050
  NO_SCHEME,
7051
7052
  /**
7053
   * @see https://url.spec.whatwg.org/#fragment-state
7054
   */
7055
  FRAGMENT,
7056
7057
  /**
7058
   * @see https://url.spec.whatwg.org/#relative-state
7059
   */
7060
  RELATIVE_SCHEME,
7061
7062
  /**
7063
   * @see https://url.spec.whatwg.org/#relative-slash-state
7064
   */
7065
  RELATIVE_SLASH,
7066
7067
  /**
7068
   * @see https://url.spec.whatwg.org/#file-state
7069
   */
7070
  FILE,
7071
7072
  /**
7073
   * @see https://url.spec.whatwg.org/#file-host-state
7074
   */
7075
  FILE_HOST,
7076
7077
  /**
7078
   * @see https://url.spec.whatwg.org/#file-slash-state
7079
   */
7080
  FILE_SLASH,
7081
7082
  /**
7083
   * @see https://url.spec.whatwg.org/#path-or-authority-state
7084
   */
7085
  PATH_OR_AUTHORITY,
7086
7087
  /**
7088
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
7089
   */
7090
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
7091
7092
  /**
7093
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
7094
   */
7095
  SPECIAL_AUTHORITY_SLASHES,
7096
7097
  /**
7098
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
7099
   */
7100
  SPECIAL_RELATIVE_OR_AUTHORITY,
7101
7102
  /**
7103
   * @see https://url.spec.whatwg.org/#query-state
7104
   */
7105
  QUERY,
7106
7107
  /**
7108
   * @see https://url.spec.whatwg.org/#path-state
7109
   */
7110
  PATH,
7111
7112
  /**
7113
   * @see https://url.spec.whatwg.org/#path-start-state
7114
   */
7115
  PATH_START,
7116
7117
  /**
7118
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
7119
   */
7120
  OPAQUE_PATH,
7121
7122
  /**
7123
   * @see https://url.spec.whatwg.org/#port-state
7124
   */
7125
  PORT,
7126
};
7127
7128
/**
7129
 * Converts a parser state to its string name for debugging.
7130
 * @param s The state to convert.
7131
 * @return A string representation of the state.
7132
 */
7133
ada_warn_unused std::string to_string(ada::state s);
7134
7135
}  // namespace ada
7136
7137
#endif  // ADA_STATE_H
7138
/* end file include/ada/state.h */
7139
/* begin file include/ada/unicode.h */
7140
/**
7141
 * @file unicode.h
7142
 * @brief Definitions for all unicode specific functions.
7143
 */
7144
#ifndef ADA_UNICODE_H
7145
#define ADA_UNICODE_H
7146
7147
7148
#include <cstddef>
7149
#include <cstdint>
7150
#include <optional>
7151
#include <string>
7152
#include <string_view>
7153
7154
/**
7155
 * Unicode operations. These functions are not part of our public API and may
7156
 * change at any time.
7157
 *
7158
 * @private
7159
 * @namespace ada::unicode
7160
 * @brief Includes the definitions for unicode operations
7161
 */
7162
namespace ada::unicode {
7163
7164
/**
7165
 * @private
7166
 * We receive a UTF-8 string representing a domain name.
7167
 * If the string is percent encoded, we apply percent decoding.
7168
 *
7169
 * Given a domain, we need to identify its labels.
7170
 * They are separated by label-separators:
7171
 *
7172
 * U+002E (.) FULL STOP
7173
 * U+FF0E FULLWIDTH FULL STOP
7174
 * U+3002 IDEOGRAPHIC FULL STOP
7175
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
7176
 *
7177
 * They are all mapped to U+002E.
7178
 *
7179
 * We process each label into a string that should not exceed 63 octets.
7180
 * If the string is already punycode (starts with "xn--"), then we must
7181
 * scan it to look for unallowed code points.
7182
 * Otherwise, if the string is not pure ASCII, we need to transcode it
7183
 * to punycode by following RFC 3454 which requires us to
7184
 * - Map characters  (see section 3),
7185
 * - Normalize (see section 4),
7186
 * - Reject forbidden characters,
7187
 * - Check for right-to-left characters and if so, check all requirements (see
7188
 * section 6),
7189
 * - Optionally reject based on unassigned code points (section 7).
7190
 *
7191
 * The Unicode standard provides a table of code points with a mapping, a list
7192
 * of forbidden code points and so forth. This table is subject to change and
7193
 * will vary based on the implementation. For Unicode 15, the table is at
7194
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
7195
 * If you use ICU, they parse this table and map it to code using a Python
7196
 * script.
7197
 *
7198
 * The resulting strings should not exceed 255 octets according to RFC 1035
7199
 * section 2.3.4. ICU checks for label size and domain size, but these errors
7200
 * are ignored.
7201
 *
7202
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
7203
 *
7204
 */
7205
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
7206
              size_t first_percent);
7207
7208
/**
7209
 * @private
7210
 * Checks if the input has tab or newline characters.
7211
 *
7212
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
7213
 * enough that compilers like GCC can 'autovectorize it'.
7214
 */
7215
ada_really_inline bool has_tabs_or_newline(
7216
    std::string_view user_input) noexcept;
7217
7218
/**
7219
 * @private
7220
 * Checks if the input is a forbidden host code point.
7221
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7222
 */
7223
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7224
7225
/**
7226
 * @private
7227
 * Checks if the input contains a forbidden domain code point.
7228
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7229
 */
7230
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7231
    const char* input, size_t length) noexcept;
7232
7233
/**
7234
 * @private
7235
 * Checks if the input contains a forbidden domain code point in which case
7236
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7237
 * then the second bit is set to 1.
7238
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7239
 */
7240
ada_really_inline constexpr uint8_t
7241
contains_forbidden_domain_code_point_or_upper(const char* input,
7242
                                              size_t length) noexcept;
7243
7244
/**
7245
 * @private
7246
 * Checks if the input is a forbidden domain code point.
7247
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7248
 */
7249
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7250
    char c) noexcept;
7251
7252
/**
7253
 * @private
7254
 * Checks if the input is alphanumeric, '+', '-' or '.'
7255
 */
7256
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7257
7258
/**
7259
 * @private
7260
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7261
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7262
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7263
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7264
 */
7265
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7266
7267
/**
7268
 * @private
7269
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7270
 * inclusive.
7271
 */
7272
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7273
7274
/**
7275
 * @private
7276
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7277
 * character.
7278
 */
7279
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7280
7281
/**
7282
 * @private
7283
 * Checks if the input is a C0 control or space character.
7284
 *
7285
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7286
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7287
 * SEPARATOR ONE, inclusive.
7288
 */
7289
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7290
7291
/**
7292
 * @private
7293
 * Checks if the input is a ASCII tab or newline character.
7294
 *
7295
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7296
 */
7297
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7298
7299
/**
7300
 * @private
7301
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7302
 * match for ".%2e", "%2e.", or "%2e%2e".
7303
 */
7304
ada_really_inline constexpr bool is_double_dot_path_segment(
7305
    std::string_view input) noexcept;
7306
7307
/**
7308
 * @private
7309
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7310
 * match for "%2e".
7311
 */
7312
ada_really_inline constexpr bool is_single_dot_path_segment(
7313
    std::string_view input) noexcept;
7314
7315
/**
7316
 * @private
7317
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7318
 */
7319
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7320
7321
/**
7322
 * @private
7323
 * @details Convert hex to binary. Caller is responsible to ensure that
7324
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7325
 */
7326
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7327
7328
/**
7329
 * @private
7330
 * first_percent should be  = input.find('%')
7331
 *
7332
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7333
 * @author Node.js
7334
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7335
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7336
 */
7337
std::string percent_decode(std::string_view input, size_t first_percent);
7338
7339
/**
7340
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7341
 * then percent-decode. Single allocation; no intermediate string.
7342
 *
7343
 * @param input A form-urlencoded component (key or value).
7344
 * @return The decoded string.
7345
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7346
 */
7347
std::string form_urlencoded_decode(std::string_view input);
7348
7349
/**
7350
 * @private
7351
 * Returns a percent-encoding string whether percent encoding was needed or not.
7352
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7353
 */
7354
std::string percent_encode(std::string_view input,
7355
                           const uint8_t character_set[]);
7356
/**
7357
 * @private
7358
 * Returns a percent-encoded string version of input, while starting the percent
7359
 * encoding at the provided index.
7360
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7361
 */
7362
std::string percent_encode(std::string_view input,
7363
                           const uint8_t character_set[], size_t index);
7364
/**
7365
 * @private
7366
 * Returns true if percent encoding was needed, in which case, we store
7367
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7368
 * true, the content is appended to 'out'.
7369
 * If percent encoding is not needed, out is left unchanged.
7370
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7371
 */
7372
template <bool append>
7373
bool percent_encode(std::string_view input, const uint8_t character_set[],
7374
                    std::string& out);
7375
/**
7376
 * @private
7377
 * Returns the index at which percent encoding should start, or (equivalently),
7378
 * the length of the prefix that does not require percent encoding.
7379
 */
7380
ada_really_inline size_t percent_encode_index(std::string_view input,
7381
                                              const uint8_t character_set[]);
7382
/**
7383
 * @private
7384
 * Lowers the string in-place, assuming that the content is ASCII.
7385
 * Return true if the content was ASCII.
7386
 */
7387
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7388
}  // namespace ada::unicode
7389
7390
#endif  // ADA_UNICODE_H
7391
/* end file include/ada/unicode.h */
7392
/* begin file include/ada/url_base-inl.h */
7393
/**
7394
 * @file url_base-inl.h
7395
 * @brief Inline functions for url base
7396
 */
7397
#ifndef ADA_URL_BASE_INL_H
7398
#define ADA_URL_BASE_INL_H
7399
7400
7401
#include <cstdint>
7402
#include <string>
7403
#if ADA_REGULAR_VISUAL_STUDIO
7404
#include <intrin.h>
7405
#endif  // ADA_REGULAR_VISUAL_STUDIO
7406
7407
namespace ada {
7408
7409
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7410
0
    const noexcept {
7411
0
  return type != ada::scheme::NOT_SPECIAL;
7412
0
}
7413
7414
0
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7415
0
  return ada::scheme::get_special_port(type);
7416
0
}
7417
7418
[[nodiscard]] ada_really_inline uint16_t
7419
0
url_base::scheme_default_port() const noexcept {
7420
0
  return scheme::get_special_port(type);
7421
0
}
7422
7423
}  // namespace ada
7424
7425
#endif  // ADA_URL_BASE_INL_H
7426
/* end file include/ada/url_base-inl.h */
7427
/* begin file include/ada/url-inl.h */
7428
/**
7429
 * @file url-inl.h
7430
 * @brief Definitions for the URL
7431
 */
7432
#ifndef ADA_URL_INL_H
7433
#define ADA_URL_INL_H
7434
7435
7436
#include <charconv>
7437
#include <cstdint>
7438
#include <cstring>
7439
#include <optional>
7440
#include <ostream>
7441
#include <string>
7442
#include <string_view>
7443
#include <system_error>
7444
#include <utility>
7445
#if ADA_REGULAR_VISUAL_STUDIO
7446
#include <intrin.h>
7447
#endif  // ADA_REGULAR_VISUAL_STUDIO
7448
7449
namespace ada {
7450
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7451
0
  return !username.empty() || !password.empty();
7452
0
}
7453
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7454
0
  return port.has_value();
7455
0
}
7456
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7457
0
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7458
0
}
7459
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7460
0
  if (!host.has_value()) {
7461
0
    return false;
7462
0
  }
7463
0
  return host->empty();
7464
0
}
7465
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7466
0
  return host.has_value();
7467
0
}
7468
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7469
0
  return out << u.to_string();
7470
0
}
7471
7472
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7473
0
  return path.size();
7474
0
}
7475
7476
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7477
0
  return path;
7478
0
}
7479
7480
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7481
0
    const {
7482
0
  url_components out{};
7483
0
7484
0
  // protocol ends with ':'. for example: "https:"
7485
0
  out.protocol_end = uint32_t(get_protocol().size());
7486
0
7487
0
  // Trailing index is always the next character of the current one.
7488
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7489
0
  size_t running_index = out.protocol_end;
7490
0
7491
0
  if (host.has_value()) {
7492
0
    // 2 characters for "//" and 1 character for starting index
7493
0
    out.host_start = out.protocol_end + 2;
7494
0
7495
0
    if (has_credentials()) {
7496
0
      out.username_end = uint32_t(out.host_start + username.size());
7497
0
7498
0
      out.host_start += uint32_t(username.size());
7499
0
7500
0
      if (!password.empty()) {
7501
0
        out.host_start += uint32_t(password.size() + 1);
7502
0
      }
7503
0
7504
0
      // host_start is the '@' when there are credentials, exactly as
7505
0
      // url_aggregator reports it, so the host itself begins one byte later.
7506
0
      out.host_end = uint32_t(out.host_start + 1 + host->size());
7507
0
    } else {
7508
0
      out.username_end = out.host_start;
7509
0
7510
0
      // Host does not start with "@" if it does not include credentials.
7511
0
      out.host_end = uint32_t(out.host_start + host->size());
7512
0
    }
7513
0
7514
0
    // host_end is one past the host, so it is already the next index.
7515
0
    running_index = out.host_end;
7516
0
  } else {
7517
0
    // Update host start and end date to the same index, since it does not
7518
0
    // exist.
7519
0
    out.host_start = out.protocol_end;
7520
0
    out.host_end = out.host_start;
7521
0
7522
0
    if (!has_opaque_path && path.starts_with("//")) {
7523
0
      // If url's host is null, url does not have an opaque path, url's path's
7524
0
      // size is greater than 1, and url's path[0] is the empty string, then
7525
0
      // append U+002F (/) followed by U+002E (.) to output.
7526
0
      running_index = out.protocol_end + 2;
7527
0
    } else {
7528
0
      running_index = out.protocol_end;
7529
0
    }
7530
0
  }
7531
0
7532
0
  if (port.has_value()) {
7533
0
    out.port = *port;
7534
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7535
0
  }
7536
0
7537
0
  out.pathname_start = uint32_t(running_index);
7538
0
7539
0
  running_index += path.size();
7540
0
7541
0
  if (query.has_value()) {
7542
0
    out.search_start = uint32_t(running_index);
7543
0
    running_index += get_search().size();
7544
0
    if (get_search().empty()) {
7545
0
      running_index++;
7546
0
    }
7547
0
  }
7548
0
7549
0
  if (hash.has_value()) {
7550
0
    out.hash_start = uint32_t(running_index);
7551
0
  }
7552
0
7553
0
  return out;
7554
0
}
7555
7556
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7557
7558
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7559
  // We do the percent encoding
7560
0
  hash = unicode::percent_encode(input,
7561
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7562
0
}
7563
7564
inline void url::update_base_search(std::string_view input,
7565
0
                                    const uint8_t query_percent_encode_set[]) {
7566
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7567
0
}
7568
7569
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7570
0
  query = std::move(input);
7571
0
}
7572
7573
0
inline void url::update_base_pathname(const std::string_view input) {
7574
0
  path = input;
7575
0
}
7576
7577
0
inline void url::update_base_username(const std::string_view input) {
7578
0
  username = input;
7579
0
}
7580
7581
0
inline void url::update_base_password(const std::string_view input) {
7582
0
  password = input;
7583
0
}
7584
7585
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7586
0
  port = input;
7587
0
}
7588
7589
0
constexpr void url::clear_pathname() { path.clear(); }
7590
7591
0
constexpr void url::clear_search() { query = std::nullopt; }
7592
7593
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7594
0
  return hash.has_value();
7595
0
}
7596
7597
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7598
0
  return query.has_value();
7599
0
}
7600
7601
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7602
7603
0
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7604
0
  type = ada::scheme::get_scheme_type(new_scheme);
7605
  // We only move the 'scheme' if it is non-special.
7606
0
  if (!is_special()) {
7607
0
    non_special_scheme = std::move(new_scheme);
7608
0
  }
7609
0
}
7610
7611
0
constexpr void url::copy_scheme(ada::url&& u) {
7612
0
  non_special_scheme = u.non_special_scheme;
7613
0
  type = u.type;
7614
0
}
7615
7616
0
constexpr void url::copy_scheme(const ada::url& u) {
7617
0
  non_special_scheme = u.non_special_scheme;
7618
0
  type = u.type;
7619
0
}
7620
7621
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7622
0
  if (is_special() && host.has_value() && username.empty() &&
7623
0
      password.empty() && !port.has_value()) [[likely]] {
7624
0
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7625
0
    const size_t host_size = host->size();
7626
0
    const size_t path_size = path.size();
7627
0
    const size_t query_size = query.has_value() ? query->size() : 0;
7628
0
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7629
0
    const size_t total = scheme.size() + 3 + host_size + path_size +
7630
0
                         (query.has_value() ? query_size + 1 : 0) +
7631
0
                         (hash.has_value() ? hash_size + 1 : 0);
7632
0
    std::string output(total, '\0');
7633
0
    char* p = output.data();
7634
0
    std::memcpy(p, scheme.data(), scheme.size());
7635
0
    p += scheme.size();
7636
0
    p[0] = ':';
7637
0
    p[1] = '/';
7638
0
    p[2] = '/';
7639
0
    p += 3;
7640
0
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7641
0
    std::memcpy(p, host->data(), host_size);
7642
0
    p += host_size;
7643
0
    std::memcpy(p, path.data(), path_size);
7644
0
    p += path_size;
7645
0
    if (query.has_value()) {
7646
0
      *p++ = '?';
7647
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7648
0
      std::memcpy(p, query->data(), query_size);
7649
0
      p += query_size;
7650
0
    }
7651
0
    if (hash.has_value()) {
7652
0
      *p++ = '#';
7653
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7654
0
      std::memcpy(p, hash->data(), hash_size);
7655
0
    }
7656
0
    return output;
7657
0
  }
7658
0
7659
0
  std::string output;
7660
0
  output.reserve(get_href_size());
7661
0
7662
0
  if (is_special()) {
7663
0
    output.append(ada::scheme::details::is_special_list[type]);
7664
0
    output += ':';
7665
0
  } else {
7666
0
    output.append(non_special_scheme);
7667
0
    output += ':';
7668
0
  }
7669
0
7670
0
  if (host.has_value()) {
7671
0
    output += '/';
7672
0
    output += '/';
7673
0
    if (has_credentials()) {
7674
0
      output.append(username);
7675
0
      if (!password.empty()) {
7676
0
        output += ':';
7677
0
        output.append(password);
7678
0
      }
7679
0
      output += '@';
7680
0
    }
7681
0
    output.append(*host);
7682
0
    if (port.has_value()) {
7683
0
      output += ':';
7684
0
      char port_buf[5];
7685
0
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7686
0
      (void)ec;
7687
0
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7688
0
    }
7689
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7690
0
    // If url's host is null, url does not have an opaque path, url's path's
7691
0
    // size is greater than 1, and url's path[0] is the empty string, then
7692
0
    // append U+002F (/) followed by U+002E (.) to output.
7693
0
    output += '/';
7694
0
    output += '.';
7695
0
  }
7696
0
  output.append(path);
7697
0
  if (query.has_value()) {
7698
0
    output += '?';
7699
0
    output.append(*query);
7700
0
  }
7701
0
  if (hash.has_value()) {
7702
0
    output += '#';
7703
0
    output.append(*hash);
7704
0
  }
7705
0
  return output;
7706
0
}
7707
7708
0
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7709
0
  size_t size = 0;
7710
0
  if (is_special()) {
7711
0
    size += ada::scheme::details::is_special_list[type].size() + 1;
7712
0
  } else {
7713
0
    size += non_special_scheme.size() + 1;
7714
0
  }
7715
0
  if (host.has_value()) {
7716
0
    size += host->size();
7717
0
    size += 2;
7718
0
    if (has_credentials()) {
7719
0
      size += username.size();
7720
0
      if (!password.empty()) {
7721
0
        size += 1 + password.size();
7722
0
      }
7723
0
      size += 1;
7724
0
    }
7725
0
    if (port.has_value()) {
7726
0
      size += 1;
7727
0
      uint16_t p = *port;
7728
0
      size += (p >= 10000)  ? 5
7729
0
              : (p >= 1000) ? 4
7730
0
              : (p >= 100)  ? 3
7731
0
              : (p >= 10)   ? 2
7732
0
                            : 1;
7733
0
    }
7734
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7735
0
    size += 2;
7736
0
  }
7737
0
  size += path.size();
7738
0
  if (query.has_value()) {
7739
0
    size += 1 + query->size();
7740
0
  }
7741
0
  if (hash.has_value()) {
7742
0
    size += 1 + hash->size();
7743
0
  }
7744
0
  return size;
7745
0
}
7746
7747
ada_really_inline size_t url::parse_port(std::string_view view,
7748
0
                                         bool check_trailing_content) noexcept {
7749
0
  ada_log("parse_port('", view, "') ", view.size());
7750
0
  if (!view.empty() && view[0] == '-') {
7751
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7752
0
    is_valid = false;
7753
0
    return 0;
7754
0
  }
7755
0
  uint16_t parsed_port{};
7756
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7757
0
  if (r.ec == std::errc::result_out_of_range) {
7758
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7759
0
    is_valid = false;
7760
0
    return 0;
7761
0
  }
7762
0
  ada_log("parse_port: ", parsed_port);
7763
0
  const auto consumed = size_t(r.ptr - view.data());
7764
0
  ada_log("parse_port: consumed ", consumed);
7765
0
  if (check_trailing_content) {
7766
0
    is_valid &=
7767
0
        (consumed == view.size() || view[consumed] == '/' ||
7768
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7769
0
  }
7770
0
  ada_log("parse_port: is_valid = ", is_valid);
7771
0
  if (is_valid) {
7772
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7773
0
    auto default_port = scheme_default_port();
7774
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7775
0
                         (default_port != parsed_port);
7776
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7777
0
                                                  : std::nullopt;
7778
0
  }
7779
0
  return consumed;
7780
0
}
7781
7782
}  // namespace ada
7783
7784
#endif  // ADA_URL_H
7785
/* end file include/ada/url-inl.h */
7786
/* begin file include/ada/url_components-inl.h */
7787
/**
7788
 * @file url_components.h
7789
 * @brief Declaration for the URL Components
7790
 */
7791
#ifndef ADA_URL_COMPONENTS_INL_H
7792
#define ADA_URL_COMPONENTS_INL_H
7793
7794
7795
#include <cstdint>
7796
7797
namespace ada {
7798
7799
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7800
0
    const noexcept {
7801
0
  /**
7802
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7803
0
   *       |     |    |          | ^^^^|       |   |
7804
0
   *       |     |    |          | |   |       |   `----- hash_start
7805
0
   *       |     |    |          | |   |       `--------- search_start
7806
0
   *       |     |    |          | |   `----------------- pathname_start
7807
0
   *       |     |    |          | `--------------------- port
7808
0
   *       |     |    |          `----------------------- host_end
7809
0
   *       |     |    `---------------------------------- host_start
7810
0
   *       |     `--------------------------------------- username_end
7811
0
   *       `--------------------------------------------- protocol_end
7812
0
   */
7813
0
  // These conditions can be made more strict.
7814
0
  if (protocol_end == url_components::omitted) {
7815
0
    return false;
7816
0
  }
7817
0
  uint32_t index = protocol_end;
7818
0
7819
0
  if (username_end == url_components::omitted) {
7820
0
    return false;
7821
0
  }
7822
0
  if (username_end < index) {
7823
0
    return false;
7824
0
  }
7825
0
  index = username_end;
7826
0
7827
0
  if (host_start == url_components::omitted) {
7828
0
    return false;
7829
0
  }
7830
0
  if (host_start < index) {
7831
0
    return false;
7832
0
  }
7833
0
  index = host_start;
7834
0
7835
0
  if (port != url_components::omitted) {
7836
0
    if (port > 0xffff) {
7837
0
      return false;
7838
0
    }
7839
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7840
0
    if (index + port_length < index) {
7841
0
      return false;
7842
0
    }
7843
0
    index += port_length;
7844
0
  }
7845
0
7846
0
  if (pathname_start == url_components::omitted) {
7847
0
    return false;
7848
0
  }
7849
0
  if (pathname_start < index) {
7850
0
    return false;
7851
0
  }
7852
0
  index = pathname_start;
7853
0
7854
0
  if (search_start != url_components::omitted) {
7855
0
    if (search_start < index) {
7856
0
      return false;
7857
0
    }
7858
0
    index = search_start;
7859
0
  }
7860
0
7861
0
  if (hash_start != url_components::omitted) {
7862
0
    if (hash_start < index) {
7863
0
      return false;
7864
0
    }
7865
0
  }
7866
0
7867
0
  return true;
7868
0
}
7869
7870
}  // namespace ada
7871
#endif
7872
/* end file include/ada/url_components-inl.h */
7873
/* begin file include/ada/url_aggregator.h */
7874
/**
7875
 * @file url_aggregator.h
7876
 * @brief Declaration for the `ada::url_aggregator` class.
7877
 *
7878
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7879
 * URL using a single buffer with component offsets. This is the default and
7880
 * most memory-efficient URL representation in Ada.
7881
 *
7882
 * @see url.h for an alternative representation using separate strings
7883
 */
7884
#ifndef ADA_URL_AGGREGATOR_H
7885
#define ADA_URL_AGGREGATOR_H
7886
7887
#include <cstddef>
7888
#include <cstdint>
7889
#include <ostream>
7890
#include <string>
7891
#include <string_view>
7892
#include <variant>
7893
7894
7895
namespace ada {
7896
7897
namespace parser {}
7898
7899
/**
7900
 * @brief Memory-efficient URL representation using a single buffer.
7901
 *
7902
 * The `url_aggregator` stores the entire normalized URL in a single string
7903
 * buffer and tracks component boundaries using offsets. This design minimizes
7904
 * memory allocations and is ideal for read-mostly access patterns.
7905
 *
7906
 * Getter methods return `std::string_view` pointing into the internal buffer.
7907
 * These views are lightweight (no allocation) but become invalid if the
7908
 * url_aggregator is modified or destroyed.
7909
 *
7910
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7911
 * when any setter is called. Do not use a getter's result as input to a
7912
 * setter on the same object without copying first.
7913
 *
7914
 * @note This is the default URL type returned by `ada::parse()`.
7915
 *
7916
 * @see url For an alternative using separate std::string instances
7917
 */
7918
struct url_aggregator : url_base {
7919
0
  url_aggregator() = default;
7920
0
  url_aggregator(const url_aggregator& u) = default;
7921
0
  url_aggregator(url_aggregator&& u) noexcept = default;
7922
0
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7923
0
  url_aggregator& operator=(const url_aggregator& u) = default;
7924
0
  ~url_aggregator() override = default;
7925
7926
  /**
7927
   * The setter functions follow the steps defined in the URL Standard.
7928
   *
7929
   * The url_aggregator has a single buffer that contains the entire normalized
7930
   * URL. The various components are represented as offsets into that buffer.
7931
   * When you call get_pathname(), for example, you get a std::string_view that
7932
   * points into that buffer. If the url_aggregator is modified, the buffer may
7933
   * be reallocated, and the std::string_view you obtained earlier may become
7934
   * invalid. In particular, this implies that you cannot modify the URL using
7935
   * a setter function with a std::string_view that points into the
7936
   * url_aggregator E.g., the following is incorrect:
7937
   * url->set_hostname(url->get_pathname()).
7938
   * You must first copy the pathname to a separate string.
7939
   * std::string pathname(url->get_pathname());
7940
   * url->set_hostname(pathname);
7941
   *
7942
   * The caller is responsible for ensuring that the url_aggregator is not
7943
   * modified while any std::string_view obtained from it is in use.
7944
   */
7945
  bool set_href(std::string_view input);
7946
  bool set_host(std::string_view input);
7947
  bool set_hostname(std::string_view input);
7948
  bool set_protocol(std::string_view input);
7949
  bool set_username(std::string_view input);
7950
  bool set_password(std::string_view input);
7951
  bool set_port(std::string_view input);
7952
  bool set_pathname(std::string_view input);
7953
  void set_search(std::string_view input);
7954
  void set_hash(std::string_view input);
7955
7956
  /**
7957
   * Validates whether the hostname is a valid domain according to RFC 1034.
7958
   * @return `true` if the domain is valid, `false` otherwise.
7959
   */
7960
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7961
7962
  /**
7963
   * Returns the URL's origin (scheme + host + port for special URLs).
7964
   * @return A newly allocated string containing the serialized origin.
7965
   * @see https://url.spec.whatwg.org/#concept-url-origin
7966
   */
7967
  [[nodiscard]] std::string get_origin() const override;
7968
7969
  /**
7970
   * Returns the full serialized URL (the href) as a string_view.
7971
   * Does not allocate memory. The returned view becomes invalid if this
7972
   * url_aggregator is modified or destroyed.
7973
   * @return A string_view into the internal buffer.
7974
   * @see https://url.spec.whatwg.org/#dom-url-href
7975
   */
7976
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7977
      ada_lifetime_bound;
7978
7979
  /**
7980
   * Returns the byte length of the serialized URL without allocating a string.
7981
   * @return Size of the href in bytes.
7982
   */
7983
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7984
7985
  /**
7986
   * Returns the URL's username component.
7987
   * Does not allocate memory. The returned view becomes invalid if this
7988
   * url_aggregator is modified or destroyed.
7989
   * @return A string_view of the username.
7990
   * @see https://url.spec.whatwg.org/#dom-url-username
7991
   */
7992
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7993
7994
  /**
7995
   * Returns the URL's password component.
7996
   * Does not allocate memory. The returned view becomes invalid if this
7997
   * url_aggregator is modified or destroyed.
7998
   * @return A string_view of the password.
7999
   * @see https://url.spec.whatwg.org/#dom-url-password
8000
   */
8001
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
8002
8003
  /**
8004
   * Returns the URL's port as a string (e.g., "8080").
8005
   * Does not allocate memory. Returns empty view if no port is set.
8006
   * The returned view becomes invalid if this url_aggregator is modified.
8007
   * @return A string_view of the port.
8008
   * @see https://url.spec.whatwg.org/#dom-url-port
8009
   */
8010
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
8011
8012
  /**
8013
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
8014
   * Does not allocate memory. Returns empty view if no fragment is set.
8015
   * The returned view becomes invalid if this url_aggregator is modified.
8016
   * @return A string_view of the hash.
8017
   * @see https://url.spec.whatwg.org/#dom-url-hash
8018
   */
8019
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
8020
8021
  /**
8022
   * Returns the URL's host and port (e.g., "example.com:8080").
8023
   * Does not allocate memory. Returns empty view if no host is set.
8024
   * The returned view becomes invalid if this url_aggregator is modified.
8025
   * @return A string_view of host:port.
8026
   * @see https://url.spec.whatwg.org/#dom-url-host
8027
   */
8028
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
8029
8030
  /**
8031
   * Returns the URL's hostname (without port).
8032
   * Does not allocate memory. Returns empty view if no host is set.
8033
   * The returned view becomes invalid if this url_aggregator is modified.
8034
   * @return A string_view of the hostname.
8035
   * @see https://url.spec.whatwg.org/#dom-url-hostname
8036
   */
8037
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
8038
8039
  /**
8040
   * Returns the URL's path component.
8041
   * Does not allocate memory. The returned view becomes invalid if this
8042
   * url_aggregator is modified or destroyed.
8043
   * @return A string_view of the pathname.
8044
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8045
   */
8046
  [[nodiscard]] constexpr std::string_view get_pathname() const
8047
      ada_lifetime_bound;
8048
8049
  /**
8050
   * Returns the byte length of the pathname without creating a string.
8051
   * @return Size of the pathname in bytes.
8052
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8053
   */
8054
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
8055
8056
  /**
8057
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
8058
   * Does not allocate memory. Returns empty view if no query is set.
8059
   * The returned view becomes invalid if this url_aggregator is modified.
8060
   * @return A string_view of the search/query.
8061
   * @see https://url.spec.whatwg.org/#dom-url-search
8062
   */
8063
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
8064
8065
  /**
8066
   * Returns the URL's scheme followed by a colon (e.g., "https:").
8067
   * Does not allocate memory. The returned view becomes invalid if this
8068
   * url_aggregator is modified or destroyed.
8069
   * @return A string_view of the protocol.
8070
   * @see https://url.spec.whatwg.org/#dom-url-protocol
8071
   */
8072
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
8073
8074
  /**
8075
   * Checks if the URL has credentials (non-empty username or password).
8076
   * @return `true` if username or password is non-empty, `false` otherwise.
8077
   */
8078
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
8079
      const noexcept;
8080
8081
  /**
8082
   * Returns the URL component offsets for efficient serialization.
8083
   *
8084
   * The components represent byte offsets into the serialized URL:
8085
   * ```
8086
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8087
   *       |     |    |          | ^^^^|       |   |
8088
   *       |     |    |          | |   |       |   `----- hash_start
8089
   *       |     |    |          | |   |       `--------- search_start
8090
   *       |     |    |          | |   `----------------- pathname_start
8091
   *       |     |    |          | `--------------------- port
8092
   *       |     |    |          `----------------------- host_end
8093
   *       |     |    `---------------------------------- host_start
8094
   *       |     `--------------------------------------- username_end
8095
   *       `--------------------------------------------- protocol_end
8096
   * ```
8097
   * @return A constant reference to the url_components struct.
8098
   * @see https://github.com/servo/rust-url
8099
   */
8100
  [[nodiscard]] ada_really_inline const url_components& get_components()
8101
      const noexcept;
8102
8103
  /**
8104
   * Returns a JSON string representation of this URL for debugging.
8105
   * @return A JSON-formatted string with all URL components.
8106
   */
8107
  [[nodiscard]] std::string to_string() const override;
8108
8109
  /**
8110
   * Returns a visual diagram showing component boundaries in the URL.
8111
   * Useful for debugging and understanding URL structure.
8112
   * @return A multi-line string diagram.
8113
   */
8114
  [[nodiscard]] std::string to_diagram() const;
8115
8116
  /**
8117
   * Validates internal consistency of component offsets (for debugging).
8118
   * @return `true` if offsets are consistent, `false` if corrupted.
8119
   */
8120
  [[nodiscard]] constexpr bool validate() const noexcept;
8121
8122
  /**
8123
   * Checks if the URL has an empty hostname (host is set but empty string).
8124
   * @return `true` if host exists but is empty, `false` otherwise.
8125
   */
8126
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
8127
8128
  /**
8129
   * Checks if the URL has a hostname (including empty hostnames).
8130
   * @return `true` if host is present, `false` otherwise.
8131
   */
8132
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
8133
8134
  /**
8135
   * Checks if the URL has a non-empty username.
8136
   * @return `true` if username is non-empty, `false` otherwise.
8137
   */
8138
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
8139
8140
  /**
8141
   * Checks if the URL has a non-empty password.
8142
   * @return `true` if password is non-empty, `false` otherwise.
8143
   */
8144
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
8145
8146
  /**
8147
   * Checks if the URL has a non-default port explicitly specified.
8148
   * @return `true` if a port is present, `false` otherwise.
8149
   */
8150
  [[nodiscard]] constexpr bool has_port() const noexcept;
8151
8152
  /**
8153
   * Checks if the URL has a password component (may be empty).
8154
   * @return `true` if password is present, `false` otherwise.
8155
   */
8156
  [[nodiscard]] constexpr bool has_password() const noexcept;
8157
8158
  /**
8159
   * Checks if the URL has a fragment/hash component.
8160
   * @return `true` if hash is present, `false` otherwise.
8161
   */
8162
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
8163
8164
  /**
8165
   * Checks if the URL has a query/search component.
8166
   * @return `true` if query is present, `false` otherwise.
8167
   */
8168
  [[nodiscard]] constexpr bool has_search() const noexcept override;
8169
8170
  /**
8171
   * Removes the port from the URL.
8172
   */
8173
  inline void clear_port();
8174
8175
  /**
8176
   * Removes the hash/fragment from the URL.
8177
   */
8178
  inline void clear_hash();
8179
8180
  /**
8181
   * Removes the query/search string from the URL.
8182
   */
8183
  inline void clear_search() override;
8184
8185
 private:
8186
  // helper methods
8187
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
8188
      url_aggregator& url);
8189
  // parse_url methods
8190
  friend url_aggregator parser::parse_url<url_aggregator>(
8191
      std::string_view, const url_aggregator*);
8192
8193
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
8194
      std::string_view, const url_aggregator*);
8195
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
8196
      std::string_view, const url_aggregator*);
8197
  template <class result_type>
8198
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
8199
  template <class result_type>
8200
  friend bool parser::finish_simple_absolute_with_port(std::string_view,
8201
                                                       result_type&,
8202
                                                       ada::scheme::type,
8203
                                                       uint32_t, size_t, size_t,
8204
                                                       size_t, bool);
8205
  template <class result_type>
8206
  friend bool parser::try_parse_simple_relative(std::string_view,
8207
                                                const result_type&,
8208
                                                result_type&);
8209
8210
#if ADA_INCLUDE_URL_PATTERN
8211
  // url_pattern methods
8212
  template <url_pattern_regex::regex_concept regex_provider>
8213
  friend tl::expected<url_pattern<regex_provider>, errors>
8214
  parse_url_pattern_impl(
8215
      std::variant<std::string_view, url_pattern_init>&& input,
8216
      const std::string_view* base_url, const url_pattern_options* options);
8217
#endif  // ADA_INCLUDE_URL_PATTERN
8218
8219
  // components is declared before buffer so that the offset fields land
8220
  // close to url_base in memory, improving cache locality for getter calls.
8221
  // Note: exact cache-line placement is implementation- and platform-dependent.
8222
  url_components components{};
8223
  std::string buffer{};
8224
8225
  /**
8226
   * Returns true if neither the search, nor the hash nor the pathname
8227
   * have been set.
8228
   * @return true if the buffer is ready to receive the path.
8229
   */
8230
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
8231
8232
  inline void add_authority_slashes_if_needed();
8233
8234
  /**
8235
   * To optimize performance, you may indicate how much memory to allocate
8236
   * within this instance.
8237
   */
8238
  constexpr void reserve(uint32_t capacity);
8239
8240
  ada_really_inline size_t parse_port(std::string_view view,
8241
                                      bool check_trailing_content) override;
8242
8243
0
  ada_really_inline size_t parse_port(std::string_view view) override {
8244
0
    return this->parse_port(view, false);
8245
0
  }
8246
8247
  /**
8248
   * Return true on success. The 'in_place' parameter indicates whether the
8249
   * the string_view input is pointing in the buffer. When in_place is false,
8250
   * we must nearly always update the buffer.
8251
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8252
   */
8253
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8254
8255
  /**
8256
   * Return true on success.
8257
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8258
   */
8259
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8260
8261
  /**
8262
   * Return true on success.
8263
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8264
   */
8265
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8266
8267
  ada_really_inline void parse_path(std::string_view input);
8268
8269
  /**
8270
   * A URL cannot have a username/password/port if its host is null or the empty
8271
   * string, or its scheme is "file".
8272
   */
8273
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8274
8275
  /**
8276
   * @private
8277
   * Several setters restore a saved copy of the URL when the mutation pushes
8278
   * the buffer past get_max_input_length(). A setter grows the buffer by at
8279
   * most input_len * 3 (worst-case percent-encoding) plus a small constant for
8280
   * delimiters, so this returns false when that upper bound stays within the
8281
   * limit. It does not account for parse-failure rollbacks, which are
8282
   * unrelated to the length limit.
8283
   */
8284
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
8285
8286
  template <bool override_hostname = false>
8287
  bool set_host_or_hostname(std::string_view input);
8288
8289
  ada_really_inline bool parse_host(std::string_view input);
8290
8291
  inline void update_base_authority(std::string_view base_buffer,
8292
                                    const url_components& base);
8293
  inline void update_unencoded_base_hash(std::string_view input);
8294
  inline void update_base_hostname(std::string_view input);
8295
  inline void update_base_search(std::string_view input);
8296
  inline void update_base_search(std::string_view input,
8297
                                 const uint8_t* query_percent_encode_set);
8298
  inline void update_base_pathname(std::string_view input);
8299
  inline void update_base_username(std::string_view input);
8300
  inline void append_base_username(std::string_view input);
8301
  inline void update_base_password(std::string_view input);
8302
  inline void append_base_password(std::string_view input);
8303
  inline void update_base_port(uint32_t input);
8304
  inline void append_base_pathname(std::string_view input);
8305
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8306
  constexpr void clear_hostname();
8307
  constexpr void clear_password();
8308
  constexpr void clear_pathname() override;
8309
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8310
  void delete_dash_dot();
8311
  inline void consume_prepared_path(std::string_view input);
8312
  template <bool has_state_override = false>
8313
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8314
      std::string_view input);
8315
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8316
                                                std::string_view input);
8317
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8318
  constexpr void set_protocol_as_file();
8319
  inline void set_scheme(std::string_view new_scheme);
8320
  /**
8321
   * Fast function to set the scheme from a view with a colon in the
8322
   * buffer, does not change type.
8323
   */
8324
  inline void set_scheme_from_view_with_colon(
8325
      std::string_view new_scheme_with_colon);
8326
  inline void copy_scheme(const url_aggregator& u);
8327
8328
  inline void update_host_to_base_host(const std::string_view input);
8329
8330
};  // url_aggregator
8331
8332
inline std::ostream& operator<<(std::ostream& out, const url& u);
8333
}  // namespace ada
8334
8335
#endif
8336
/* end file include/ada/url_aggregator.h */
8337
/* begin file include/ada/url_aggregator-inl.h */
8338
/**
8339
 * @file url_aggregator-inl.h
8340
 * @brief Inline functions for url aggregator
8341
 */
8342
#ifndef ADA_URL_AGGREGATOR_INL_H
8343
#define ADA_URL_AGGREGATOR_INL_H
8344
8345
/* begin file include/ada/unicode-inl.h */
8346
/**
8347
 * @file unicode-inl.h
8348
 * @brief Definitions for unicode operations.
8349
 */
8350
#ifndef ADA_UNICODE_INL_H
8351
#define ADA_UNICODE_INL_H
8352
8353
#include <cstddef>
8354
#include <cstdint>
8355
#include <cstring>
8356
#include <string_view>
8357
8358
/**
8359
 * Unicode operations. These functions are not part of our public API and may
8360
 * change at any time.
8361
 *
8362
 * private
8363
 * @namespace ada::unicode
8364
 * @brief Includes the declarations for unicode operations
8365
 */
8366
namespace ada::unicode {
8367
ada_really_inline size_t percent_encode_index(const std::string_view input,
8368
0
                                              const uint8_t character_set[]) {
8369
0
  const char* data = input.data();
8370
0
  const size_t size = input.size();
8371
8372
  // Process 8 bytes at a time using unrolled loop
8373
0
  size_t i = 0;
8374
0
  for (; i + 8 <= size; i += 8) {
8375
0
    unsigned char chunk[8];
8376
0
    std::memcpy(&chunk, data + i,
8377
0
                8);  // entices compiler to unconditionally process 8 characters
8378
8379
    // Check 8 characters at once
8380
0
    for (size_t j = 0; j < 8; j++) {
8381
0
      if (character_sets::bit_at(character_set, chunk[j])) {
8382
0
        return i + j;
8383
0
      }
8384
0
    }
8385
0
  }
8386
8387
  // Handle remaining bytes
8388
0
  for (; i < size; i++) {
8389
0
    if (character_sets::bit_at(character_set, data[i])) {
8390
0
      return i;
8391
0
    }
8392
0
  }
8393
8394
0
  return size;
8395
0
}
8396
}  // namespace ada::unicode
8397
8398
#endif  // ADA_UNICODE_INL_H
8399
/* end file include/ada/unicode-inl.h */
8400
8401
#include <charconv>
8402
#include <cstdint>
8403
#include <cstring>
8404
#include <ostream>
8405
#include <string>
8406
#include <string_view>
8407
#include <system_error>
8408
8409
namespace ada {
8410
8411
inline void url_aggregator::update_base_authority(
8412
0
    std::string_view base_buffer, const ada::url_components& base) {
8413
0
  std::string_view input = base_buffer.substr(
8414
0
      base.protocol_end, base.host_start - base.protocol_end);
8415
0
  ada_log("url_aggregator::update_base_authority ", input);
8416
8417
0
  bool input_starts_with_dash = input.starts_with("//");
8418
0
  uint32_t diff = components.host_start - components.protocol_end;
8419
8420
0
  buffer.erase(components.protocol_end,
8421
0
               components.host_start - components.protocol_end);
8422
0
  components.username_end = components.protocol_end;
8423
8424
0
  if (input_starts_with_dash) {
8425
0
    input.remove_prefix(2);
8426
0
    diff += 2;  // add "//"
8427
0
    buffer.insert(components.protocol_end, "//");
8428
0
    components.username_end += 2;
8429
0
  }
8430
8431
0
  size_t password_delimiter = input.find(':');
8432
8433
  // Check if input contains both username and password by checking the
8434
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8435
0
  if (password_delimiter != std::string_view::npos) {
8436
    // Insert both username and password
8437
0
    std::string_view username = input.substr(0, password_delimiter);
8438
0
    std::string_view password = input.substr(password_delimiter + 1);
8439
8440
0
    buffer.insert(components.protocol_end + diff, username);
8441
0
    diff += uint32_t(username.size());
8442
0
    buffer.insert(components.protocol_end + diff, ":");
8443
0
    components.username_end = components.protocol_end + diff;
8444
0
    buffer.insert(components.protocol_end + diff + 1, password);
8445
0
    diff += uint32_t(password.size()) + 1;
8446
0
  } else if (!input.empty()) {
8447
    // Insert only username
8448
0
    buffer.insert(components.protocol_end + diff, input);
8449
0
    components.username_end =
8450
0
        components.protocol_end + diff + uint32_t(input.size());
8451
0
    diff += uint32_t(input.size());
8452
0
  }
8453
8454
0
  components.host_start += diff;
8455
8456
0
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8457
0
    buffer.insert(components.host_start, "@");
8458
0
    diff++;
8459
0
  }
8460
0
  components.host_end += diff;
8461
0
  components.pathname_start += diff;
8462
0
  if (components.search_start != url_components::omitted) {
8463
0
    components.search_start += diff;
8464
0
  }
8465
0
  if (components.hash_start != url_components::omitted) {
8466
0
    components.hash_start += diff;
8467
0
  }
8468
0
}
8469
8470
0
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8471
0
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8472
0
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8473
0
          " bytes] components.hash_start = ", components.hash_start);
8474
0
  ADA_ASSERT_TRUE(validate());
8475
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8476
0
  if (components.hash_start != url_components::omitted) {
8477
0
    buffer.resize(components.hash_start);
8478
0
  }
8479
0
  components.hash_start = uint32_t(buffer.size());
8480
0
  buffer += "#";
8481
0
  bool encoding_required = unicode::percent_encode<true>(
8482
0
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8483
  // When encoding_required is false, then buffer is left unchanged, and percent
8484
  // encoding was not deemed required.
8485
0
  if (!encoding_required) {
8486
0
    buffer.append(input);
8487
0
  }
8488
0
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8489
0
          buffer, "' [", buffer.size(), " bytes]");
8490
0
  ADA_ASSERT_TRUE(validate());
8491
0
}
8492
8493
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8494
0
    uint32_t start, uint32_t end, std::string_view input) {
8495
0
  uint32_t current_length = end - start;
8496
0
  uint32_t input_size = uint32_t(input.size());
8497
0
  uint32_t new_difference = input_size - current_length;
8498
8499
0
  if (current_length == 0) {
8500
0
    buffer.insert(start, input);
8501
0
  } else if (input_size == current_length) {
8502
0
    if (input_size != 0) {
8503
0
      std::memmove(buffer.data() + start, input.data(), input_size);
8504
0
    }
8505
0
  } else if (input_size < current_length) {
8506
0
    buffer.erase(start, current_length - input_size);
8507
0
    buffer.replace(start, input_size, input);
8508
0
  } else {
8509
0
    buffer.replace(start, current_length, input.substr(0, current_length));
8510
0
    buffer.insert(start + current_length, input.substr(current_length));
8511
0
  }
8512
8513
0
  return new_difference;
8514
0
}
8515
8516
0
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8517
0
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8518
0
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8519
0
  ADA_ASSERT_TRUE(validate());
8520
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8521
8522
  // This next line is required for when parsing a URL like `foo://`
8523
0
  add_authority_slashes_if_needed();
8524
8525
0
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8526
0
  uint32_t new_difference =
8527
0
      replace_and_resize(components.host_start, components.host_end, input);
8528
8529
0
  if (has_credentials) {
8530
0
    buffer.insert(components.host_start, "@");
8531
0
    new_difference++;
8532
0
  }
8533
0
  components.host_end += new_difference;
8534
0
  components.pathname_start += new_difference;
8535
0
  if (components.search_start != url_components::omitted) {
8536
0
    components.search_start += new_difference;
8537
0
  }
8538
0
  if (components.hash_start != url_components::omitted) {
8539
0
    components.hash_start += new_difference;
8540
0
  }
8541
0
  ADA_ASSERT_TRUE(validate());
8542
0
}
8543
8544
[[nodiscard]] ada_really_inline uint32_t
8545
0
url_aggregator::get_pathname_length() const noexcept {
8546
0
  ada_log("url_aggregator::get_pathname_length");
8547
0
  uint32_t ending_index = uint32_t(buffer.size());
8548
0
  if (components.search_start != url_components::omitted) {
8549
0
    ending_index = components.search_start;
8550
0
  } else if (components.hash_start != url_components::omitted) {
8551
0
    ending_index = components.hash_start;
8552
0
  }
8553
0
  return ending_index - components.pathname_start;
8554
0
}
8555
8556
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8557
0
    const noexcept {
8558
0
  return buffer.size() == components.pathname_start;
8559
0
}
8560
8561
0
inline void url_aggregator::update_base_search(std::string_view input) {
8562
0
  ada_log("url_aggregator::update_base_search ", input);
8563
0
  ADA_ASSERT_TRUE(validate());
8564
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8565
0
  if (input.empty()) {
8566
0
    clear_search();
8567
0
    return;
8568
0
  }
8569
8570
0
  if (input[0] == '?') {
8571
0
    input.remove_prefix(1);
8572
0
  }
8573
8574
0
  if (components.hash_start == url_components::omitted) {
8575
0
    if (components.search_start == url_components::omitted) {
8576
0
      components.search_start = uint32_t(buffer.size());
8577
0
      buffer += "?";
8578
0
    } else {
8579
0
      buffer.resize(components.search_start + 1);
8580
0
    }
8581
8582
0
    buffer.append(input);
8583
0
  } else if (components.search_start != url_components::omitted) {
8584
0
    const uint32_t difference = replace_and_resize(
8585
0
        components.search_start + 1, components.hash_start, input);
8586
0
    components.hash_start += difference;
8587
0
  } else {
8588
0
    components.search_start = components.hash_start;
8589
0
    buffer.insert(components.search_start, input.size() + 1, '?');
8590
0
    if (!input.empty()) {
8591
0
      std::memmove(buffer.data() + components.search_start + 1, input.data(),
8592
0
                   input.size());
8593
0
    }
8594
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8595
0
  }
8596
8597
0
  ADA_ASSERT_TRUE(validate());
8598
0
}
8599
8600
inline void url_aggregator::update_base_search(
8601
0
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8602
0
  ada_log("url_aggregator::update_base_search ", input,
8603
0
          " with encoding parameter ", to_string(), "\n", to_diagram());
8604
0
  ADA_ASSERT_TRUE(validate());
8605
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8606
8607
0
  if (components.hash_start == url_components::omitted) {
8608
0
    if (components.search_start == url_components::omitted) {
8609
0
      components.search_start = uint32_t(buffer.size());
8610
0
      buffer += "?";
8611
0
    } else {
8612
0
      buffer.resize(components.search_start + 1);
8613
0
    }
8614
8615
0
    bool encoding_required =
8616
0
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8617
    // When encoding_required is false, then buffer is left unchanged, and
8618
    // percent encoding was not deemed required.
8619
0
    if (!encoding_required) {
8620
0
      buffer.append(input);
8621
0
    }
8622
0
  } else {
8623
0
    const size_t idx =
8624
0
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8625
0
    std::string encoded;
8626
0
    std::string_view replacement = input;
8627
0
    if (idx != input.size()) {
8628
0
      encoded =
8629
0
          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
8630
0
      replacement = encoded;
8631
0
    }
8632
8633
0
    if (components.search_start != url_components::omitted) {
8634
0
      const uint32_t difference = replace_and_resize(
8635
0
          components.search_start + 1, components.hash_start, replacement);
8636
0
      components.hash_start += difference;
8637
0
    } else {
8638
0
      components.search_start = components.hash_start;
8639
0
      buffer.insert(components.search_start, replacement.size() + 1, '?');
8640
0
      if (!replacement.empty()) {
8641
0
        std::memmove(buffer.data() + components.search_start + 1,
8642
0
                     replacement.data(), replacement.size());
8643
0
      }
8644
0
      components.hash_start +=
8645
0
          uint32_t(replacement.size() + 1);  // Do not forget `?`
8646
0
    }
8647
0
  }
8648
8649
0
  ADA_ASSERT_TRUE(validate());
8650
0
}
8651
8652
0
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8653
0
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8654
0
          " bytes] \n", to_diagram());
8655
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8656
0
  ADA_ASSERT_TRUE(validate());
8657
8658
0
  const bool begins_with_dashdash = input.starts_with("//");
8659
0
  if (!begins_with_dashdash && has_dash_dot()) {
8660
    // We must delete the ./
8661
0
    delete_dash_dot();
8662
0
  }
8663
8664
0
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8665
0
      !has_dash_dot()) {
8666
    // If url's host is null, url does not have an opaque path, url's path's
8667
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8668
    // output.
8669
0
    buffer.insert(components.pathname_start, "/.");
8670
0
    components.pathname_start += 2;
8671
0
    if (components.search_start != url_components::omitted) {
8672
0
      components.search_start += 2;
8673
0
    }
8674
0
    if (components.hash_start != url_components::omitted) {
8675
0
      components.hash_start += 2;
8676
0
    }
8677
0
  }
8678
8679
0
  uint32_t difference = replace_and_resize(
8680
0
      components.pathname_start,
8681
0
      components.pathname_start + get_pathname_length(), input);
8682
0
  if (components.search_start != url_components::omitted) {
8683
0
    components.search_start += difference;
8684
0
  }
8685
0
  if (components.hash_start != url_components::omitted) {
8686
0
    components.hash_start += difference;
8687
0
  }
8688
0
  ADA_ASSERT_TRUE(validate());
8689
0
}
8690
8691
0
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8692
0
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8693
0
          "\n", to_diagram());
8694
0
  ADA_ASSERT_TRUE(validate());
8695
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8696
#if ADA_DEVELOPMENT_CHECKS
8697
  // computing the expected password.
8698
  std::string path_expected(get_pathname());
8699
  path_expected.append(input);
8700
#endif  // ADA_DEVELOPMENT_CHECKS
8701
0
  uint32_t ending_index = uint32_t(buffer.size());
8702
0
  if (components.search_start != url_components::omitted) {
8703
0
    ending_index = components.search_start;
8704
0
  } else if (components.hash_start != url_components::omitted) {
8705
0
    ending_index = components.hash_start;
8706
0
  }
8707
0
  buffer.insert(ending_index, input);
8708
8709
0
  if (components.search_start != url_components::omitted) {
8710
0
    components.search_start += uint32_t(input.size());
8711
0
  }
8712
0
  if (components.hash_start != url_components::omitted) {
8713
0
    components.hash_start += uint32_t(input.size());
8714
0
  }
8715
#if ADA_DEVELOPMENT_CHECKS
8716
  std::string path_after = std::string(get_pathname());
8717
  ADA_ASSERT_EQUAL(
8718
      path_expected, path_after,
8719
      "append_base_pathname problem after inserting " + std::string(input));
8720
#endif  // ADA_DEVELOPMENT_CHECKS
8721
0
  ADA_ASSERT_TRUE(validate());
8722
0
}
8723
8724
0
inline void url_aggregator::update_base_username(const std::string_view input) {
8725
0
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8726
0
          "\n", to_diagram());
8727
0
  ADA_ASSERT_TRUE(validate());
8728
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8729
8730
0
  add_authority_slashes_if_needed();
8731
8732
0
  bool has_password = has_non_empty_password();
8733
0
  bool host_starts_with_at = buffer.size() > components.host_start &&
8734
0
                             buffer[components.host_start] == '@';
8735
0
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8736
0
                                     components.username_end, input);
8737
8738
0
  components.username_end += diff;
8739
0
  components.host_start += diff;
8740
8741
0
  if (!input.empty() && !host_starts_with_at) {
8742
0
    buffer.insert(components.host_start, "@");
8743
0
    diff++;
8744
0
  } else if (input.empty() && host_starts_with_at && !has_password) {
8745
    // Input is empty, there is no password, and we need to remove "@" from
8746
    // hostname
8747
0
    buffer.erase(components.host_start, 1);
8748
0
    diff--;
8749
0
  }
8750
8751
0
  components.host_end += diff;
8752
0
  components.pathname_start += diff;
8753
0
  if (components.search_start != url_components::omitted) {
8754
0
    components.search_start += diff;
8755
0
  }
8756
0
  if (components.hash_start != url_components::omitted) {
8757
0
    components.hash_start += diff;
8758
0
  }
8759
0
  ADA_ASSERT_TRUE(validate());
8760
0
}
8761
8762
0
inline void url_aggregator::append_base_username(const std::string_view input) {
8763
0
  ada_log("url_aggregator::append_base_username ", input);
8764
0
  ADA_ASSERT_TRUE(validate());
8765
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8766
#if ADA_DEVELOPMENT_CHECKS
8767
  // computing the expected password.
8768
  std::string username_expected(get_username());
8769
  username_expected.append(input);
8770
#endif  // ADA_DEVELOPMENT_CHECKS
8771
0
  add_authority_slashes_if_needed();
8772
8773
  // If input is empty, do nothing.
8774
0
  if (input.empty()) {
8775
0
    return;
8776
0
  }
8777
8778
0
  uint32_t difference = uint32_t(input.size());
8779
0
  buffer.insert(components.username_end, input);
8780
0
  components.username_end += difference;
8781
0
  components.host_start += difference;
8782
8783
0
  if (buffer[components.host_start] != '@' &&
8784
0
      components.host_start != components.host_end) {
8785
0
    buffer.insert(components.host_start, "@");
8786
0
    difference++;
8787
0
  }
8788
8789
0
  components.host_end += difference;
8790
0
  components.pathname_start += difference;
8791
0
  if (components.search_start != url_components::omitted) {
8792
0
    components.search_start += difference;
8793
0
  }
8794
0
  if (components.hash_start != url_components::omitted) {
8795
0
    components.hash_start += difference;
8796
0
  }
8797
#if ADA_DEVELOPMENT_CHECKS
8798
  std::string username_after(get_username());
8799
  ADA_ASSERT_EQUAL(
8800
      username_expected, username_after,
8801
      "append_base_username problem after inserting " + std::string(input));
8802
#endif  // ADA_DEVELOPMENT_CHECKS
8803
0
  ADA_ASSERT_TRUE(validate());
8804
0
}
8805
8806
0
constexpr void url_aggregator::clear_password() {
8807
0
  ada_log("url_aggregator::clear_password ", to_string());
8808
0
  ADA_ASSERT_TRUE(validate());
8809
0
  if (!has_password()) {
8810
0
    return;
8811
0
  }
8812
8813
0
  uint32_t diff = components.host_start - components.username_end;
8814
0
  buffer.erase(components.username_end, diff);
8815
0
  components.host_start -= diff;
8816
0
  components.host_end -= diff;
8817
0
  components.pathname_start -= diff;
8818
0
  if (components.search_start != url_components::omitted) {
8819
0
    components.search_start -= diff;
8820
0
  }
8821
0
  if (components.hash_start != url_components::omitted) {
8822
0
    components.hash_start -= diff;
8823
0
  }
8824
0
}
8825
8826
0
inline void url_aggregator::update_base_password(const std::string_view input) {
8827
0
  ada_log("url_aggregator::update_base_password ", input);
8828
0
  ADA_ASSERT_TRUE(validate());
8829
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8830
8831
0
  add_authority_slashes_if_needed();
8832
8833
  // TODO: Optimization opportunity. Merge the following removal functions.
8834
0
  if (input.empty()) {
8835
0
    clear_password();
8836
8837
    // Remove username too, if it is empty.
8838
0
    if (!has_non_empty_username()) {
8839
0
      update_base_username("");
8840
0
    }
8841
8842
0
    return;
8843
0
  }
8844
8845
0
  bool password_exists = has_password();
8846
0
  uint32_t difference = uint32_t(input.size());
8847
8848
0
  if (password_exists) {
8849
0
    difference = replace_and_resize(components.username_end + 1,
8850
0
                                    components.host_start, input);
8851
0
  } else {
8852
0
    buffer.insert(components.username_end, input.size() + 1, ':');
8853
0
    std::memmove(buffer.data() + components.username_end + 1, input.data(),
8854
0
                 input.size());
8855
0
    difference++;
8856
0
  }
8857
0
  components.host_start += difference;
8858
8859
  // The following line is required to add "@" to hostname. When updating
8860
  // password if hostname does not start with "@", it is "update_base_password"s
8861
  // responsibility to set it.
8862
0
  if (buffer[components.host_start] != '@') {
8863
0
    buffer.insert(components.host_start, "@");
8864
0
    difference++;
8865
0
  }
8866
8867
0
  components.host_end += difference;
8868
0
  components.pathname_start += difference;
8869
0
  if (components.search_start != url_components::omitted) {
8870
0
    components.search_start += difference;
8871
0
  }
8872
0
  if (components.hash_start != url_components::omitted) {
8873
0
    components.hash_start += difference;
8874
0
  }
8875
0
  ADA_ASSERT_TRUE(validate());
8876
0
}
8877
8878
0
inline void url_aggregator::append_base_password(const std::string_view input) {
8879
0
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8880
0
          "\n", to_diagram());
8881
0
  ADA_ASSERT_TRUE(validate());
8882
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8883
#if ADA_DEVELOPMENT_CHECKS
8884
  // computing the expected password.
8885
  std::string password_expected = std::string(get_password());
8886
  password_expected.append(input);
8887
#endif  // ADA_DEVELOPMENT_CHECKS
8888
0
  add_authority_slashes_if_needed();
8889
8890
  // If input is empty, do nothing.
8891
0
  if (input.empty()) {
8892
0
    return;
8893
0
  }
8894
8895
0
  uint32_t difference = uint32_t(input.size());
8896
0
  if (has_password()) {
8897
0
    buffer.insert(components.host_start, input);
8898
0
  } else {
8899
0
    difference++;  // Increment for ":"
8900
0
    buffer.insert(components.username_end, ":");
8901
0
    buffer.insert(components.username_end + 1, input);
8902
0
  }
8903
0
  components.host_start += difference;
8904
8905
  // The following line is required to add "@" to hostname. When updating
8906
  // password if hostname does not start with "@", it is "append_base_password"s
8907
  // responsibility to set it.
8908
0
  if (buffer[components.host_start] != '@') {
8909
0
    buffer.insert(components.host_start, "@");
8910
0
    difference++;
8911
0
  }
8912
8913
0
  components.host_end += difference;
8914
0
  components.pathname_start += difference;
8915
0
  if (components.search_start != url_components::omitted) {
8916
0
    components.search_start += difference;
8917
0
  }
8918
0
  if (components.hash_start != url_components::omitted) {
8919
0
    components.hash_start += difference;
8920
0
  }
8921
#if ADA_DEVELOPMENT_CHECKS
8922
  std::string password_after(get_password());
8923
  ADA_ASSERT_EQUAL(
8924
      password_expected, password_after,
8925
      "append_base_password problem after inserting " + std::string(input));
8926
#endif  // ADA_DEVELOPMENT_CHECKS
8927
0
  ADA_ASSERT_TRUE(validate());
8928
0
}
8929
8930
0
inline void url_aggregator::update_base_port(uint32_t input) {
8931
0
  ada_log("url_aggregator::update_base_port");
8932
0
  ADA_ASSERT_TRUE(validate());
8933
0
  if (input == url_components::omitted) {
8934
0
    clear_port();
8935
0
    return;
8936
0
  }
8937
  // calling std::to_string(input.value()) is unfortunate given that the port
8938
  // value is probably already available as a string.
8939
0
  std::string value = helpers::concat(":", std::to_string(input));
8940
0
  uint32_t difference = uint32_t(value.size());
8941
8942
0
  if (components.port != url_components::omitted) {
8943
0
    difference -= components.pathname_start - components.host_end;
8944
0
    buffer.erase(components.host_end,
8945
0
                 components.pathname_start - components.host_end);
8946
0
  }
8947
8948
0
  buffer.insert(components.host_end, value);
8949
0
  components.pathname_start += difference;
8950
0
  if (components.search_start != url_components::omitted) {
8951
0
    components.search_start += difference;
8952
0
  }
8953
0
  if (components.hash_start != url_components::omitted) {
8954
0
    components.hash_start += difference;
8955
0
  }
8956
0
  components.port = input;
8957
0
  ADA_ASSERT_TRUE(validate());
8958
0
}
8959
8960
0
inline void url_aggregator::clear_port() {
8961
0
  ada_log("url_aggregator::clear_port");
8962
0
  ADA_ASSERT_TRUE(validate());
8963
0
  if (components.port == url_components::omitted) {
8964
0
    return;
8965
0
  }
8966
0
  uint32_t length = components.pathname_start - components.host_end;
8967
0
  buffer.erase(components.host_end, length);
8968
0
  components.pathname_start -= length;
8969
0
  if (components.search_start != url_components::omitted) {
8970
0
    components.search_start -= length;
8971
0
  }
8972
0
  if (components.hash_start != url_components::omitted) {
8973
0
    components.hash_start -= length;
8974
0
  }
8975
0
  components.port = url_components::omitted;
8976
0
  ADA_ASSERT_TRUE(validate());
8977
0
}
8978
8979
0
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8980
0
  ada_log("url_aggregator::retrieve_base_port");
8981
0
  return components.port;
8982
0
}
8983
8984
0
inline void url_aggregator::clear_search() {
8985
0
  ada_log("url_aggregator::clear_search");
8986
0
  ADA_ASSERT_TRUE(validate());
8987
0
  if (components.search_start == url_components::omitted) {
8988
0
    return;
8989
0
  }
8990
8991
0
  if (components.hash_start == url_components::omitted) {
8992
0
    buffer.resize(components.search_start);
8993
0
  } else {
8994
0
    buffer.erase(components.search_start,
8995
0
                 components.hash_start - components.search_start);
8996
0
    components.hash_start = components.search_start;
8997
0
  }
8998
8999
0
  components.search_start = url_components::omitted;
9000
9001
#if ADA_DEVELOPMENT_CHECKS
9002
  ADA_ASSERT_EQUAL(get_search(), "",
9003
                   "search should have been cleared on buffer=" + buffer +
9004
                       " with " + components.to_string() + "\n" + to_diagram());
9005
#endif
9006
0
  ADA_ASSERT_TRUE(validate());
9007
0
}
9008
9009
0
inline void url_aggregator::clear_hash() {
9010
0
  ada_log("url_aggregator::clear_hash");
9011
0
  ADA_ASSERT_TRUE(validate());
9012
0
  if (components.hash_start == url_components::omitted) {
9013
0
    return;
9014
0
  }
9015
0
  buffer.resize(components.hash_start);
9016
0
  components.hash_start = url_components::omitted;
9017
9018
#if ADA_DEVELOPMENT_CHECKS
9019
  ADA_ASSERT_EQUAL(get_hash(), "",
9020
                   "hash should have been cleared on buffer=" + buffer +
9021
                       " with " + components.to_string() + "\n" + to_diagram());
9022
#endif
9023
0
  ADA_ASSERT_TRUE(validate());
9024
0
}
9025
9026
0
constexpr void url_aggregator::clear_pathname() {
9027
0
  ada_log("url_aggregator::clear_pathname");
9028
0
  ADA_ASSERT_TRUE(validate());
9029
0
  uint32_t ending_index = uint32_t(buffer.size());
9030
0
  if (components.search_start != url_components::omitted) {
9031
0
    ending_index = components.search_start;
9032
0
  } else if (components.hash_start != url_components::omitted) {
9033
0
    ending_index = components.hash_start;
9034
0
  }
9035
0
  uint32_t pathname_length = ending_index - components.pathname_start;
9036
0
  buffer.erase(components.pathname_start, pathname_length);
9037
0
  uint32_t difference = pathname_length;
9038
0
  if (components.pathname_start == components.host_end + 2 &&
9039
0
      buffer[components.host_end] == '/' &&
9040
0
      buffer[components.host_end + 1] == '.') {
9041
0
    components.pathname_start -= 2;
9042
0
    buffer.erase(components.host_end, 2);
9043
0
    difference += 2;
9044
0
  }
9045
0
  if (components.search_start != url_components::omitted) {
9046
0
    components.search_start -= difference;
9047
0
  }
9048
0
  if (components.hash_start != url_components::omitted) {
9049
0
    components.hash_start -= difference;
9050
0
  }
9051
0
  ada_log("url_aggregator::clear_pathname completed, running checks...");
9052
#if ADA_DEVELOPMENT_CHECKS
9053
  ADA_ASSERT_EQUAL(get_pathname(), "",
9054
                   "pathname should have been cleared on buffer=" + buffer +
9055
                       " with " + components.to_string() + "\n" + to_diagram());
9056
#endif
9057
0
  ADA_ASSERT_TRUE(validate());
9058
0
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
9059
0
}
9060
9061
0
constexpr void url_aggregator::clear_hostname() {
9062
0
  ada_log("url_aggregator::clear_hostname");
9063
0
  ADA_ASSERT_TRUE(validate());
9064
0
  if (!has_authority()) {
9065
0
    return;
9066
0
  }
9067
0
  ADA_ASSERT_TRUE(has_authority());
9068
9069
0
  uint32_t hostname_length = components.host_end - components.host_start;
9070
0
  uint32_t start = components.host_start;
9071
9072
  // If hostname starts with "@", we should not remove that character.
9073
0
  if (hostname_length > 0 && buffer[start] == '@') {
9074
0
    start++;
9075
0
    hostname_length--;
9076
0
  }
9077
0
  buffer.erase(start, hostname_length);
9078
0
  components.host_end = start;
9079
0
  components.pathname_start -= hostname_length;
9080
0
  if (components.search_start != url_components::omitted) {
9081
0
    components.search_start -= hostname_length;
9082
0
  }
9083
0
  if (components.hash_start != url_components::omitted) {
9084
0
    components.hash_start -= hostname_length;
9085
0
  }
9086
#if ADA_DEVELOPMENT_CHECKS
9087
  ADA_ASSERT_EQUAL(get_hostname(), "",
9088
                   "hostname should have been cleared on buffer=" + buffer +
9089
                       " with " + components.to_string() + "\n" + to_diagram());
9090
#endif
9091
0
  ADA_ASSERT_TRUE(has_authority());
9092
0
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
9093
0
                   "hostname should have been cleared on buffer=" + buffer +
9094
0
                       " with " + components.to_string() + "\n" + to_diagram());
9095
0
  ADA_ASSERT_TRUE(validate());
9096
0
}
9097
9098
0
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
9099
0
  ada_log("url_aggregator::has_hash");
9100
0
  return components.hash_start != url_components::omitted;
9101
0
}
9102
9103
0
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
9104
0
  ada_log("url_aggregator::has_search");
9105
0
  return components.search_start != url_components::omitted;
9106
0
}
9107
9108
0
constexpr bool url_aggregator::has_credentials() const noexcept {
9109
0
  ada_log("url_aggregator::has_credentials");
9110
0
  return has_non_empty_username() || has_non_empty_password();
9111
0
}
9112
9113
0
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
9114
0
  ada_log("url_aggregator::cannot_have_credentials_or_port");
9115
0
  return type == ada::scheme::type::FILE ||
9116
0
         components.host_start == components.host_end;
9117
0
}
9118
9119
[[nodiscard]] ada_really_inline const ada::url_components&
9120
0
url_aggregator::get_components() const noexcept {
9121
0
  return components;
9122
0
}
9123
9124
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
9125
0
    const noexcept {
9126
0
  ada_log("url_aggregator::has_authority");
9127
  // Performance: instead of doing this potentially expensive check, we could
9128
  // have a boolean in the struct.
9129
0
  return components.protocol_end + 2 <= components.host_start &&
9130
0
         buffer[components.protocol_end] == '/' &&
9131
0
         buffer[components.protocol_end + 1] == '/';
9132
0
}
9133
9134
0
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
9135
0
  ada_log("url_aggregator::add_authority_slashes_if_needed");
9136
0
  ADA_ASSERT_TRUE(validate());
9137
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
9138
  // to insert
9139
  // `//` initially to the buffer, since it depends on the hostname existence.
9140
0
  if (has_authority()) {
9141
0
    return;
9142
0
  }
9143
  // Performance: the common case is components.protocol_end == buffer.size()
9144
  // Optimization opportunity: in many cases, the "//" is part of the input and
9145
  // the insert could be fused with another insert.
9146
0
  buffer.insert(components.protocol_end, "//");
9147
0
  components.username_end += 2;
9148
0
  components.host_start += 2;
9149
0
  components.host_end += 2;
9150
0
  components.pathname_start += 2;
9151
0
  if (components.search_start != url_components::omitted) {
9152
0
    components.search_start += 2;
9153
0
  }
9154
0
  if (components.hash_start != url_components::omitted) {
9155
0
    components.hash_start += 2;
9156
0
  }
9157
0
  ADA_ASSERT_TRUE(validate());
9158
0
}
9159
9160
0
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
9161
0
  buffer.reserve(capacity);
9162
0
}
9163
9164
0
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
9165
0
  ada_log("url_aggregator::has_non_empty_username");
9166
0
  return components.protocol_end + 2 < components.username_end;
9167
0
}
9168
9169
0
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
9170
0
  ada_log("url_aggregator::has_non_empty_password");
9171
0
  return components.host_start > components.username_end;
9172
0
}
9173
9174
0
constexpr bool url_aggregator::has_password() const noexcept {
9175
0
  ada_log("url_aggregator::has_password");
9176
  // This function does not care about the length of the password
9177
0
  return components.host_start > components.username_end &&
9178
0
         buffer[components.username_end] == ':';
9179
0
}
9180
9181
0
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
9182
0
  if (!has_hostname()) {
9183
0
    return false;
9184
0
  }
9185
0
  if (components.host_start == components.host_end) {
9186
0
    return true;
9187
0
  }
9188
0
  if (components.host_end > components.host_start + 1) {
9189
0
    return false;
9190
0
  }
9191
0
  return components.username_end != components.host_start;
9192
0
}
9193
9194
0
constexpr bool url_aggregator::has_hostname() const noexcept {
9195
0
  return has_authority();
9196
0
}
9197
9198
0
constexpr bool url_aggregator::has_port() const noexcept {
9199
0
  ada_log("url_aggregator::has_port");
9200
  // A URL cannot have a username/password/port if its host is null or the empty
9201
  // string, or its scheme is "file".
9202
0
  return has_hostname() && components.pathname_start != components.host_end;
9203
0
}
9204
9205
0
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
9206
  // If url's host is null, url does not have an opaque path, url's path's size
9207
  // is greater than 1, and url's path[0] is the empty string, then append
9208
  // U+002F (/) followed by U+002E (.) to output.
9209
0
  ada_log("url_aggregator::has_dash_dot");
9210
#if ADA_DEVELOPMENT_CHECKS
9211
  // If pathname_start and host_end are exactly two characters apart, then we
9212
  // either have a one-digit port such as http://test.com:5?param=1 or else we
9213
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
9214
  if (components.pathname_start == components.host_end + 2) {
9215
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
9216
                     buffer[components.host_end + 1] == '.') ||
9217
                    (buffer[components.host_end] == ':' &&
9218
                     checkers::is_digit(buffer[components.host_end + 1])));
9219
  }
9220
  if (components.pathname_start == components.host_end + 2 &&
9221
      buffer[components.host_end] == '/' &&
9222
      buffer[components.host_end + 1] == '.') {
9223
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
9224
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
9225
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
9226
  }
9227
#endif
9228
  // Performance: it should be uncommon for components.pathname_start ==
9229
  // components.host_end + 2 to be true. So we put this check first in the
9230
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
9231
  // more expensive, but should be uncommon.
9232
0
  return components.pathname_start == components.host_end + 2 &&
9233
0
         !has_opaque_path && buffer[components.host_end] == '/' &&
9234
0
         buffer[components.host_end + 1] == '.';
9235
0
}
9236
9237
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
9238
0
    const noexcept ada_lifetime_bound {
9239
0
  ada_log("url_aggregator::get_href");
9240
0
  return buffer;
9241
0
}
9242
9243
0
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
9244
0
  return buffer.size();
9245
0
}
9246
9247
ada_really_inline size_t
9248
0
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
9249
0
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
9250
0
  if (!view.empty() && view[0] == '-') {
9251
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
9252
0
    is_valid = false;
9253
0
    return 0;
9254
0
  }
9255
0
  uint16_t parsed_port{};
9256
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
9257
0
  if (r.ec == std::errc::result_out_of_range) {
9258
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9259
0
    is_valid = false;
9260
0
    return 0;
9261
0
  }
9262
0
  ada_log("parse_port: ", parsed_port);
9263
0
  const size_t consumed = size_t(r.ptr - view.data());
9264
0
  ada_log("parse_port: consumed ", consumed);
9265
0
  if (check_trailing_content) {
9266
0
    is_valid &=
9267
0
        (consumed == view.size() || view[consumed] == '/' ||
9268
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9269
0
  }
9270
0
  ada_log("parse_port: is_valid = ", is_valid);
9271
0
  if (is_valid) {
9272
0
    ada_log("parse_port", r.ec == std::errc());
9273
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9274
0
    auto default_port = scheme_default_port();
9275
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9276
0
                         (default_port != parsed_port);
9277
0
    if (r.ec == std::errc() && is_port_valid) {
9278
0
      update_base_port(parsed_port);
9279
0
    } else {
9280
0
      clear_port();
9281
0
    }
9282
0
  }
9283
0
  return consumed;
9284
0
}
9285
9286
0
constexpr void url_aggregator::set_protocol_as_file() {
9287
0
  ada_log("url_aggregator::set_protocol_as_file ");
9288
0
  ADA_ASSERT_TRUE(validate());
9289
0
  type = ada::scheme::type::FILE;
9290
  // next line could overflow but unsigned arithmetic has well-defined
9291
  // overflows.
9292
0
  uint32_t new_difference = 5 - components.protocol_end;
9293
9294
0
  if (buffer.empty()) {
9295
0
    buffer.append("file:");
9296
0
  } else {
9297
0
    buffer.erase(0, components.protocol_end);
9298
0
    buffer.insert(0, "file:");
9299
0
  }
9300
0
  components.protocol_end = 5;
9301
9302
  // Update the rest of the components.
9303
0
  components.username_end += new_difference;
9304
0
  components.host_start += new_difference;
9305
0
  components.host_end += new_difference;
9306
0
  components.pathname_start += new_difference;
9307
0
  if (components.search_start != url_components::omitted) {
9308
0
    components.search_start += new_difference;
9309
0
  }
9310
0
  if (components.hash_start != url_components::omitted) {
9311
0
    components.hash_start += new_difference;
9312
0
  }
9313
0
  ADA_ASSERT_TRUE(validate());
9314
0
}
9315
9316
0
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9317
0
  if (!is_valid) {
9318
0
    return true;
9319
0
  }
9320
0
  if (!components.check_offset_consistency()) {
9321
0
    ada_log("url_aggregator::validate inconsistent components \n",
9322
0
            to_diagram());
9323
0
    return false;
9324
0
  }
9325
0
  // We have a credible components struct, but let us investivate more
9326
0
  // carefully:
9327
0
  /**
9328
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9329
0
   *       |     |    |          | ^^^^|       |   |
9330
0
   *       |     |    |          | |   |       |   `----- hash_start
9331
0
   *       |     |    |          | |   |       `--------- search_start
9332
0
   *       |     |    |          | |   `----------------- pathname_start
9333
0
   *       |     |    |          | `--------------------- port
9334
0
   *       |     |    |          `----------------------- host_end
9335
0
   *       |     |    `---------------------------------- host_start
9336
0
   *       |     `--------------------------------------- username_end
9337
0
   *       `--------------------------------------------- protocol_end
9338
0
   */
9339
0
  if (components.protocol_end == url_components::omitted) {
9340
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9341
0
    return false;
9342
0
  }
9343
0
  if (components.username_end == url_components::omitted) {
9344
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9345
0
    return false;
9346
0
  }
9347
0
  if (components.host_start == url_components::omitted) {
9348
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9349
0
    return false;
9350
0
  }
9351
0
  if (components.host_end == url_components::omitted) {
9352
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9353
0
    return false;
9354
0
  }
9355
0
  if (components.pathname_start == url_components::omitted) {
9356
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9357
0
    return false;
9358
0
  }
9359
0
9360
0
  if (components.protocol_end > buffer.size()) {
9361
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9362
0
    return false;
9363
0
  }
9364
0
  if (components.username_end > buffer.size()) {
9365
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9366
0
    return false;
9367
0
  }
9368
0
  if (components.host_start > buffer.size()) {
9369
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9370
0
    return false;
9371
0
  }
9372
0
  if (components.host_end > buffer.size()) {
9373
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9374
0
    return false;
9375
0
  }
9376
0
  if (components.pathname_start > buffer.size()) {
9377
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9378
0
            to_diagram());
9379
0
    return false;
9380
0
  }
9381
0
9382
0
  if (components.protocol_end > 0) {
9383
0
    if (buffer[components.protocol_end - 1] != ':') {
9384
0
      ada_log(
9385
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9386
0
          to_diagram());
9387
0
      return false;
9388
0
    }
9389
0
  }
9390
0
9391
0
  if (components.username_end != buffer.size() &&
9392
0
      components.username_end > components.protocol_end + 2) {
9393
0
    if (buffer[components.username_end] != ':' &&
9394
0
        buffer[components.username_end] != '@') {
9395
0
      ada_log(
9396
0
          "url_aggregator::validate missing : or @ at the end of the username "
9397
0
          "\n",
9398
0
          to_diagram());
9399
0
      return false;
9400
0
    }
9401
0
  }
9402
0
9403
0
  if (components.host_start != buffer.size()) {
9404
0
    if (components.host_start > components.username_end) {
9405
0
      if (buffer[components.host_start] != '@') {
9406
0
        ada_log(
9407
0
            "url_aggregator::validate missing @ at the end of the password \n",
9408
0
            to_diagram());
9409
0
        return false;
9410
0
      }
9411
0
    } else if (components.host_start == components.username_end &&
9412
0
               components.host_end > components.host_start) {
9413
0
      if (components.host_start == components.protocol_end + 2) {
9414
0
        if (buffer[components.protocol_end] != '/' ||
9415
0
            buffer[components.protocol_end + 1] != '/') {
9416
0
          ada_log(
9417
0
              "url_aggregator::validate missing // between protocol and host "
9418
0
              "\n",
9419
0
              to_diagram());
9420
0
          return false;
9421
0
        }
9422
0
      } else {
9423
0
        if (components.host_start > components.protocol_end &&
9424
0
            buffer[components.host_start] != '@') {
9425
0
          ada_log(
9426
0
              "url_aggregator::validate missing @ at the end of the username "
9427
0
              "\n",
9428
0
              to_diagram());
9429
0
          return false;
9430
0
        }
9431
0
      }
9432
0
    } else {
9433
0
      if (components.host_end != components.host_start) {
9434
0
        ada_log("url_aggregator::validate expected omitted host \n",
9435
0
                to_diagram());
9436
0
        return false;
9437
0
      }
9438
0
    }
9439
0
  }
9440
0
  if (components.host_end != buffer.size() &&
9441
0
      components.pathname_start > components.host_end) {
9442
0
    if (components.pathname_start == components.host_end + 2 &&
9443
0
        buffer[components.host_end] == '/' &&
9444
0
        buffer[components.host_end + 1] == '.') {
9445
0
      if (components.pathname_start + 1 >= buffer.size() ||
9446
0
          buffer[components.pathname_start] != '/' ||
9447
0
          buffer[components.pathname_start + 1] != '/') {
9448
0
        ada_log(
9449
0
            "url_aggregator::validate expected the path to begin with // \n",
9450
0
            to_diagram());
9451
0
        return false;
9452
0
      }
9453
0
    } else if (buffer[components.host_end] != ':') {
9454
0
      ada_log("url_aggregator::validate missing : at the port \n",
9455
0
              to_diagram());
9456
0
      return false;
9457
0
    }
9458
0
  }
9459
0
  if (components.pathname_start != buffer.size() &&
9460
0
      components.pathname_start < components.search_start &&
9461
0
      components.pathname_start < components.hash_start && !has_opaque_path) {
9462
0
    if (buffer[components.pathname_start] != '/') {
9463
0
      ada_log("url_aggregator::validate missing / at the path \n",
9464
0
              to_diagram());
9465
0
      return false;
9466
0
    }
9467
0
  }
9468
0
  if (components.search_start != url_components::omitted) {
9469
0
    if (buffer[components.search_start] != '?') {
9470
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9471
0
              to_diagram());
9472
0
      return false;
9473
0
    }
9474
0
  }
9475
0
  if (components.hash_start != url_components::omitted) {
9476
0
    if (buffer[components.hash_start] != '#') {
9477
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9478
0
              to_diagram());
9479
0
      return false;
9480
0
    }
9481
0
  }
9482
0
9483
0
  return true;
9484
0
}
9485
9486
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9487
0
    ada_lifetime_bound {
9488
0
  ada_log("url_aggregator::get_pathname pathname_start = ",
9489
0
          components.pathname_start, " buffer.size() = ", buffer.size(),
9490
0
          " components.search_start = ", components.search_start,
9491
0
          " components.hash_start = ", components.hash_start);
9492
0
  auto ending_index = uint32_t(buffer.size());
9493
0
  if (components.search_start != url_components::omitted) {
9494
0
    ending_index = components.search_start;
9495
0
  } else if (components.hash_start != url_components::omitted) {
9496
0
    ending_index = components.hash_start;
9497
0
  }
9498
0
  return helpers::substring(buffer, components.pathname_start, ending_index);
9499
0
}
9500
9501
inline std::ostream& operator<<(std::ostream& out,
9502
0
                                const ada::url_aggregator& u) {
9503
0
  return out << u.to_string();
9504
0
}
9505
9506
0
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9507
0
  ada_log("url_aggregator::update_host_to_base_host ", input);
9508
0
  ADA_ASSERT_TRUE(validate());
9509
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9510
0
  if (type != ada::scheme::type::FILE) {
9511
    // Let host be the result of host parsing host_view with url is not special.
9512
0
    if (input.empty() && !is_special()) {
9513
0
      if (has_hostname()) {
9514
0
        clear_hostname();
9515
0
      } else if (has_dash_dot()) {
9516
0
        add_authority_slashes_if_needed();
9517
0
        delete_dash_dot();
9518
0
      }
9519
0
      return;
9520
0
    }
9521
0
  }
9522
0
  update_base_hostname(input);
9523
0
  ADA_ASSERT_TRUE(validate());
9524
0
  return;
9525
0
}
9526
}  // namespace ada
9527
9528
#endif  // ADA_URL_AGGREGATOR_INL_H
9529
/* end file include/ada/url_aggregator-inl.h */
9530
/* begin file include/ada/url_search_params.h */
9531
/**
9532
 * @file url_search_params.h
9533
 * @brief URL query string parameter manipulation.
9534
 *
9535
 * This file provides the `url_search_params` class for parsing, manipulating,
9536
 * and serializing URL query strings. It implements the URLSearchParams API
9537
 * from the WHATWG URL Standard.
9538
 *
9539
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9540
 */
9541
#ifndef ADA_URL_SEARCH_PARAMS_H
9542
#define ADA_URL_SEARCH_PARAMS_H
9543
9544
#include <cstddef>
9545
#include <optional>
9546
#include <string>
9547
#include <string_view>
9548
#include <utility>
9549
#include <vector>
9550
9551
namespace ada {
9552
9553
/**
9554
 * @brief Iterator types for url_search_params iteration.
9555
 */
9556
enum class url_search_params_iter_type {
9557
  KEYS,    /**< Iterate over parameter keys only */
9558
  VALUES,  /**< Iterate over parameter values only */
9559
  ENTRIES, /**< Iterate over key-value pairs */
9560
};
9561
9562
template <typename T, url_search_params_iter_type Type>
9563
struct url_search_params_iter;
9564
9565
/** Type alias for a key-value pair of string views. */
9566
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9567
9568
/** Iterator over search parameter keys. */
9569
using url_search_params_keys_iter =
9570
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9571
/** Iterator over search parameter values. */
9572
using url_search_params_values_iter =
9573
    url_search_params_iter<std::string_view,
9574
                           url_search_params_iter_type::VALUES>;
9575
/** Iterator over search parameter key-value pairs. */
9576
using url_search_params_entries_iter =
9577
    url_search_params_iter<key_value_view_pair,
9578
                           url_search_params_iter_type::ENTRIES>;
9579
9580
/**
9581
 * @brief Class for parsing and manipulating URL query strings.
9582
 *
9583
 * The `url_search_params` class provides methods to parse, modify, and
9584
 * serialize URL query parameters (the part after '?' in a URL). It handles
9585
 * percent-encoding and decoding automatically.
9586
 *
9587
 * All string inputs must be valid UTF-8. The caller is responsible for
9588
 * ensuring UTF-8 validity.
9589
 *
9590
 * Construction and `reset` refuse query strings longer than
9591
 * `get_max_input_length()` (the object is left empty). Individual `append` /
9592
 * `set` calls are not length-capped.
9593
 *
9594
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9595
 */
9596
struct url_search_params {
9597
  url_search_params() = default;
9598
9599
  /**
9600
   * Constructs url_search_params by parsing a query string.
9601
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9602
   *        If longer than `get_max_input_length()`, the object stays empty.
9603
   */
9604
0
  explicit url_search_params(const std::string_view input) {
9605
0
    initialize(input);
9606
0
  }
9607
9608
  url_search_params(const url_search_params& u) = default;
9609
0
  url_search_params(url_search_params&& u) noexcept = default;
9610
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9611
  url_search_params& operator=(const url_search_params& u) = default;
9612
0
  ~url_search_params() = default;
9613
9614
  /**
9615
   * Returns the number of key-value pairs.
9616
   * @return The total count of parameters.
9617
   */
9618
  [[nodiscard]] inline size_t size() const noexcept;
9619
9620
  /**
9621
   * Appends a new key-value pair to the parameter list.
9622
   * @param key The parameter name (must be valid UTF-8).
9623
   * @param value The parameter value (must be valid UTF-8).
9624
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9625
   */
9626
  inline void append(std::string_view key, std::string_view value);
9627
9628
  /**
9629
   * Removes all pairs with the given key.
9630
   * @param key The parameter name to remove.
9631
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9632
   */
9633
  inline void remove(std::string_view key);
9634
9635
  /**
9636
   * Removes all pairs with the given key and value.
9637
   * @param key The parameter name.
9638
   * @param value The parameter value to match.
9639
   */
9640
  inline void remove(std::string_view key, std::string_view value);
9641
9642
  /**
9643
   * Returns the value of the first pair with the given key.
9644
   * @param key The parameter name to search for.
9645
   * @return The value if found, or std::nullopt if not present.
9646
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9647
   */
9648
  inline std::optional<std::string_view> get(std::string_view key);
9649
9650
  /**
9651
   * Returns all values for pairs with the given key.
9652
   * @param key The parameter name to search for.
9653
   * @return A vector of all matching values (may be empty).
9654
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9655
   */
9656
  inline std::vector<std::string> get_all(std::string_view key);
9657
9658
  /**
9659
   * Checks if any pair has the given key.
9660
   * @param key The parameter name to search for.
9661
   * @return `true` if at least one pair has this key.
9662
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9663
   */
9664
  inline bool has(std::string_view key) noexcept;
9665
9666
  /**
9667
   * Checks if any pair matches the given key and value.
9668
   * @param key The parameter name to search for.
9669
   * @param value The parameter value to match.
9670
   * @return `true` if a matching pair exists.
9671
   */
9672
  inline bool has(std::string_view key, std::string_view value) noexcept;
9673
9674
  /**
9675
   * Sets a parameter value, replacing any existing pairs with the same key.
9676
   * @param key The parameter name (must be valid UTF-8).
9677
   * @param value The parameter value (must be valid UTF-8).
9678
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9679
   */
9680
  inline void set(std::string_view key, std::string_view value);
9681
9682
  /**
9683
   * Sorts all key-value pairs by their keys using code unit comparison.
9684
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9685
   */
9686
  inline void sort();
9687
9688
  /**
9689
   * Serializes the parameters to a query string (without leading '?').
9690
   * @return The percent-encoded query string.
9691
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9692
   */
9693
  inline std::string to_string() const;
9694
9695
  /**
9696
   * Returns an iterator over all parameter keys.
9697
   * Keys may repeat if there are duplicate parameters.
9698
   * @return An iterator yielding string_view keys.
9699
   * @note The iterator is invalidated if this object is modified.
9700
   */
9701
  inline url_search_params_keys_iter get_keys();
9702
9703
  /**
9704
   * Returns an iterator over all parameter values.
9705
   * @return An iterator yielding string_view values.
9706
   * @note The iterator is invalidated if this object is modified.
9707
   */
9708
  inline url_search_params_values_iter get_values();
9709
9710
  /**
9711
   * Returns an iterator over all key-value pairs.
9712
   * @return An iterator yielding key-value pair views.
9713
   * @note The iterator is invalidated if this object is modified.
9714
   */
9715
  inline url_search_params_entries_iter get_entries();
9716
9717
  /**
9718
   * C++ style conventional iterator support. const only because we
9719
   * do not really want the params to be modified via the iterator.
9720
   */
9721
0
  inline auto begin() const { return params.begin(); }
9722
0
  inline auto end() const { return params.end(); }
9723
0
  inline auto front() const { return params.front(); }
9724
0
  inline auto back() const { return params.back(); }
9725
0
  inline auto operator[](size_t index) const { return params[index]; }
9726
9727
  /**
9728
   * @private
9729
   * Used to reset the search params to a new input.
9730
   * Used primarily for C API.
9731
   * @param input
9732
   */
9733
  void reset(std::string_view input);
9734
9735
 private:
9736
  typedef std::pair<std::string, std::string> key_value_pair;
9737
  std::vector<key_value_pair> params{};
9738
9739
  /**
9740
   * The init parameter must be valid UTF-8.
9741
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9742
   */
9743
  void initialize(std::string_view init);
9744
9745
  template <typename T, url_search_params_iter_type Type>
9746
  friend struct url_search_params_iter;
9747
};  // url_search_params
9748
9749
/**
9750
 * @brief JavaScript-style iterator for url_search_params.
9751
 *
9752
 * Provides a `next()` method that returns successive values until exhausted.
9753
 * This matches the iterator pattern used in the Web Platform.
9754
 *
9755
 * @tparam T The type of value returned by the iterator.
9756
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9757
 *
9758
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9759
 */
9760
template <typename T, url_search_params_iter_type Type>
9761
struct url_search_params_iter {
9762
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()
9763
  url_search_params_iter(const url_search_params_iter& u) = default;
9764
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9765
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9766
      default;
9767
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9768
  ~url_search_params_iter() = default;
9769
9770
  /**
9771
   * Returns the next value in the iteration sequence.
9772
   * @return The next value, or std::nullopt if iteration is complete.
9773
   */
9774
  inline std::optional<T> next();
9775
9776
  /**
9777
   * Checks if more values are available.
9778
   * @return `true` if `next()` will return a value, `false` if exhausted.
9779
   */
9780
  inline bool has_next() const;
9781
9782
 private:
9783
  static url_search_params EMPTY;
9784
0
  inline url_search_params_iter(url_search_params& params_) : params(params_) {}
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(ada::url_search_params&)
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(ada::url_search_params&)
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(ada::url_search_params&)
9785
9786
  url_search_params& params;
9787
  size_t pos = 0;
9788
9789
  friend struct url_search_params;
9790
};
9791
9792
}  // namespace ada
9793
#endif
9794
/* end file include/ada/url_search_params.h */
9795
/* begin file include/ada/url_search_params-inl.h */
9796
/**
9797
 * @file url_search_params-inl.h
9798
 * @brief Inline declarations for the URL Search Params
9799
 */
9800
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9801
#define ADA_URL_SEARCH_PARAMS_INL_H
9802
9803
9804
#include <algorithm>
9805
#include <cstddef>
9806
#include <cstdint>
9807
#include <iterator>
9808
#include <optional>
9809
#include <ranges>
9810
#include <string>
9811
#include <string_view>
9812
#include <vector>
9813
9814
namespace ada {
9815
9816
// Declared in implementation.h; used here as a DoS bound on untrusted query
9817
// strings (ada.h includes both headers).
9818
uint32_t get_max_input_length();
9819
9820
// A default, empty url_search_params for use with empty iterators.
9821
template <typename T, ada::url_search_params_iter_type Type>
9822
url_search_params url_search_params_iter<T, Type>::EMPTY;
9823
9824
0
inline void url_search_params::reset(std::string_view input) {
9825
0
  params.clear();
9826
0
  initialize(input);
9827
0
}
9828
9829
0
inline void url_search_params::initialize(std::string_view input) {
9830
0
  if (!input.empty() && input.front() == '?') {
9831
0
    input.remove_prefix(1);
9832
0
  }
9833
0
  if (input.empty()) {
9834
0
    return;
9835
0
  }
9836
  // Refuse overlong query strings (same process-wide cap as URL parsing).
9837
0
  if (input.size() > get_max_input_length()) {
9838
0
    return;
9839
0
  }
9840
9841
0
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9842
9843
0
  auto process_key_value = [&](const std::string_view current) {
9844
0
    const auto equal = current.find('=');
9845
0
    if (equal == std::string_view::npos) {
9846
0
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9847
0
    } else {
9848
0
      params.emplace_back(
9849
0
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9850
0
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9851
0
    }
9852
0
  };
9853
9854
0
  while (!input.empty()) {
9855
0
    const auto ampersand_index = input.find('&');
9856
9857
0
    if (ampersand_index == std::string_view::npos) {
9858
0
      if (!input.empty()) {
9859
0
        process_key_value(input);
9860
0
      }
9861
0
      break;
9862
0
    } else if (ampersand_index != 0) {
9863
0
      process_key_value(input.substr(0, ampersand_index));
9864
0
    }
9865
9866
0
    input.remove_prefix(ampersand_index + 1);
9867
0
  }
9868
0
}
9869
9870
inline void url_search_params::append(const std::string_view key,
9871
0
                                      const std::string_view value) {
9872
0
  params.emplace_back(key, value);
9873
0
}
9874
9875
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9876
9877
inline std::optional<std::string_view> url_search_params::get(
9878
0
    const std::string_view key) {
9879
0
  auto entry = std::ranges::find_if(
9880
0
      params, [&key](const auto& param) { return param.first == key; });
9881
9882
0
  if (entry == params.end()) {
9883
0
    return std::nullopt;
9884
0
  }
9885
9886
0
  return entry->second;
9887
0
}
9888
9889
inline std::vector<std::string> url_search_params::get_all(
9890
0
    const std::string_view key) {
9891
0
  std::vector<std::string> out{};
9892
9893
0
  for (auto& param : params) {
9894
0
    if (param.first == key) {
9895
0
      out.emplace_back(param.second);
9896
0
    }
9897
0
  }
9898
9899
0
  return out;
9900
0
}
9901
9902
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9903
0
  auto entry = std::ranges::find_if(
9904
0
      params, [&key](const auto& param) { return param.first == key; });
9905
0
  return entry != params.end();
9906
0
}
9907
9908
inline bool url_search_params::has(std::string_view key,
9909
0
                                   std::string_view value) noexcept {
9910
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9911
0
    return param.first == key && param.second == value;
9912
0
  });
9913
0
  return entry != params.end();
9914
0
}
9915
9916
0
inline std::string url_search_params::to_string() const {
9917
0
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9918
0
  std::string out{};
9919
0
  for (size_t i = 0; i < params.size(); i++) {
9920
0
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9921
0
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9922
9923
    // Performance optimization: Move this inside percent_encode.
9924
0
    std::ranges::replace(key, ' ', '+');
9925
0
    std::ranges::replace(value, ' ', '+');
9926
9927
0
    if (i != 0) {
9928
0
      out += "&";
9929
0
    }
9930
0
    out.append(key);
9931
0
    out += "=";
9932
0
    out.append(value);
9933
0
  }
9934
0
  return out;
9935
0
}
9936
9937
inline void url_search_params::set(const std::string_view key,
9938
0
                                   const std::string_view value) {
9939
0
  const auto find = [&key](const auto& param) { return param.first == key; };
9940
9941
0
  auto it = std::ranges::find_if(params, find);
9942
9943
0
  if (it == params.end()) {
9944
0
    params.emplace_back(key, value);
9945
0
  } else {
9946
0
    it->second = value;
9947
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9948
0
                 params.end());
9949
0
  }
9950
0
}
9951
9952
0
inline void url_search_params::remove(const std::string_view key) {
9953
0
  std::erase_if(params,
9954
0
                [&key](const auto& param) { return param.first == key; });
9955
0
}
9956
9957
inline void url_search_params::remove(const std::string_view key,
9958
0
                                      const std::string_view value) {
9959
0
  std::erase_if(params, [&key, &value](const auto& param) {
9960
0
    return param.first == key && param.second == value;
9961
0
  });
9962
0
}
9963
9964
0
inline void url_search_params::sort() {
9965
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9966
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9967
0
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9968
0
                                      const key_value_pair& rhs) {
9969
0
    size_t i = 0, j = 0;
9970
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9971
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9972
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9973
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9974
9975
0
      if (low_surrogate1 != 0) {
9976
0
        codePoint1 = low_surrogate1;
9977
0
        low_surrogate1 = 0;
9978
0
      } else {
9979
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9980
0
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9981
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9982
0
          i += 2;
9983
0
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9984
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9985
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9986
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9987
0
          i += 3;
9988
0
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9989
0
          codePoint1 = ((c1 & 0x07) << 18) |
9990
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9991
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9992
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9993
0
          i += 4;
9994
9995
0
          codePoint1 -= 0x10000;
9996
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9997
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9998
0
          codePoint1 = high_surrogate;
9999
0
        } else {
10000
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10001
0
          codePoint1 = c1;
10002
0
          i++;
10003
0
        }
10004
0
      }
10005
10006
0
      if (low_surrogate2 != 0) {
10007
0
        codePoint2 = low_surrogate2;
10008
0
        low_surrogate2 = 0;
10009
0
      } else {
10010
0
        uint8_t c2 = uint8_t(rhs.first[j]);
10011
0
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
10012
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
10013
0
          j += 2;
10014
0
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
10015
0
          codePoint2 = ((c2 & 0x0F) << 12) |
10016
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
10017
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
10018
0
          j += 3;
10019
0
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
10020
0
          codePoint2 = ((c2 & 0x07) << 18) |
10021
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
10022
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
10023
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
10024
0
          j += 4;
10025
0
          codePoint2 -= 0x10000;
10026
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
10027
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
10028
0
          codePoint2 = high_surrogate;
10029
0
        } else {
10030
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10031
0
          codePoint2 = c2;
10032
0
          j++;
10033
0
        }
10034
0
      }
10035
10036
0
      if (codePoint1 != codePoint2) {
10037
0
        return (codePoint1 < codePoint2);
10038
0
      }
10039
0
    }
10040
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
10041
0
  });
10042
0
}
10043
10044
0
inline url_search_params_keys_iter url_search_params::get_keys() {
10045
0
  return url_search_params_keys_iter(*this);
10046
0
}
10047
10048
/**
10049
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10050
 */
10051
0
inline url_search_params_values_iter url_search_params::get_values() {
10052
0
  return url_search_params_values_iter(*this);
10053
0
}
10054
10055
/**
10056
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10057
 */
10058
0
inline url_search_params_entries_iter url_search_params::get_entries() {
10059
0
  return url_search_params_entries_iter(*this);
10060
0
}
10061
10062
template <typename T, url_search_params_iter_type Type>
10063
0
inline bool url_search_params_iter<T, Type>::has_next() const {
10064
0
  return pos < params.params.size();
10065
0
}
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>::has_next() const
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>::has_next() const
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>::has_next() const
10066
10067
template <>
10068
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
10069
0
  if (!has_next()) {
10070
0
    return std::nullopt;
10071
0
  }
10072
0
  return params.params[pos++].first;
10073
0
}
10074
10075
template <>
10076
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
10077
0
  if (!has_next()) {
10078
0
    return std::nullopt;
10079
0
  }
10080
0
  return params.params[pos++].second;
10081
0
}
10082
10083
template <>
10084
inline std::optional<key_value_view_pair>
10085
0
url_search_params_entries_iter::next() {
10086
0
  if (!has_next()) {
10087
0
    return std::nullopt;
10088
0
  }
10089
0
  return params.params[pos++];
10090
0
}
10091
10092
}  // namespace ada
10093
10094
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
10095
/* end file include/ada/url_search_params-inl.h */
10096
10097
/* begin file include/ada/url_pattern-inl.h */
10098
/**
10099
 * @file url_pattern-inl.h
10100
 * @brief Declaration for the URLPattern inline functions.
10101
 */
10102
#ifndef ADA_URL_PATTERN_INL_H
10103
#define ADA_URL_PATTERN_INL_H
10104
10105
10106
#include <algorithm>
10107
#include <cstddef>
10108
#include <optional>
10109
#include <string>
10110
#include <string_view>
10111
#include <utility>
10112
#include <variant>
10113
#include <vector>
10114
10115
#if ADA_INCLUDE_URL_PATTERN
10116
namespace ada {
10117
10118
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
10119
0
  return protocol == other.protocol && username == other.username &&
10120
0
         password == other.password && hostname == other.hostname &&
10121
0
         port == other.port && search == other.search && hash == other.hash &&
10122
0
         pathname == other.pathname;
10123
0
}
10124
10125
inline bool url_pattern_component_result::operator==(
10126
0
    const url_pattern_component_result& other) const {
10127
0
  return input == other.input && groups == other.groups;
10128
0
}
10129
10130
template <url_pattern_regex::regex_concept regex_provider>
10131
url_pattern_component_result
10132
url_pattern_component<regex_provider>::create_component_match_result(
10133
    std::string&& input,
10134
    std::vector<std::optional<std::string>>&& exec_result) {
10135
  // Let result be a new URLPatternComponentResult.
10136
  // Set result["input"] to input.
10137
  // Let groups be a record<USVString, (USVString or undefined)>.
10138
  auto result =
10139
      url_pattern_component_result{.input = std::move(input), .groups = {}};
10140
10141
  // We explicitly start iterating from 0 even though the spec
10142
  // says we should start from 1. This case is handled by the
10143
  // std_regex_provider which removes the full match from index 0.
10144
  // Use min() to guard against potential mismatches between
10145
  // exec_result size and group_name_list size.
10146
  const size_t size = std::min(exec_result.size(), group_name_list.size());
10147
  result.groups.reserve(size);
10148
  for (size_t index = 0; index < size; index++) {
10149
    result.groups.emplace(group_name_list[index],
10150
                          std::move(exec_result[index]));
10151
  }
10152
  return result;
10153
}
10154
10155
template <url_pattern_regex::regex_concept regex_provider>
10156
std::string_view url_pattern<regex_provider>::get_protocol() const
10157
    ada_lifetime_bound {
10158
  // Return this's associated URL pattern's protocol component's pattern string.
10159
  return protocol_component.pattern;
10160
}
10161
template <url_pattern_regex::regex_concept regex_provider>
10162
std::string_view url_pattern<regex_provider>::get_username() const
10163
    ada_lifetime_bound {
10164
  // Return this's associated URL pattern's username component's pattern string.
10165
  return username_component.pattern;
10166
}
10167
template <url_pattern_regex::regex_concept regex_provider>
10168
std::string_view url_pattern<regex_provider>::get_password() const
10169
    ada_lifetime_bound {
10170
  // Return this's associated URL pattern's password component's pattern string.
10171
  return password_component.pattern;
10172
}
10173
template <url_pattern_regex::regex_concept regex_provider>
10174
std::string_view url_pattern<regex_provider>::get_hostname() const
10175
    ada_lifetime_bound {
10176
  // Return this's associated URL pattern's hostname component's pattern string.
10177
  return hostname_component.pattern;
10178
}
10179
template <url_pattern_regex::regex_concept regex_provider>
10180
std::string_view url_pattern<regex_provider>::get_port() const
10181
    ada_lifetime_bound {
10182
  // Return this's associated URL pattern's port component's pattern string.
10183
  return port_component.pattern;
10184
}
10185
template <url_pattern_regex::regex_concept regex_provider>
10186
std::string_view url_pattern<regex_provider>::get_pathname() const
10187
    ada_lifetime_bound {
10188
  // Return this's associated URL pattern's pathname component's pattern string.
10189
  return pathname_component.pattern;
10190
}
10191
template <url_pattern_regex::regex_concept regex_provider>
10192
std::string_view url_pattern<regex_provider>::get_search() const
10193
    ada_lifetime_bound {
10194
  // Return this's associated URL pattern's search component's pattern string.
10195
  return search_component.pattern;
10196
}
10197
template <url_pattern_regex::regex_concept regex_provider>
10198
std::string_view url_pattern<regex_provider>::get_hash() const
10199
    ada_lifetime_bound {
10200
  // Return this's associated URL pattern's hash component's pattern string.
10201
  return hash_component.pattern;
10202
}
10203
template <url_pattern_regex::regex_concept regex_provider>
10204
bool url_pattern<regex_provider>::ignore_case() const {
10205
  return ignore_case_;
10206
}
10207
template <url_pattern_regex::regex_concept regex_provider>
10208
bool url_pattern<regex_provider>::has_regexp_groups() const {
10209
  // If this's associated URL pattern's has regexp groups, then return true.
10210
  return protocol_component.has_regexp_groups ||
10211
         username_component.has_regexp_groups ||
10212
         password_component.has_regexp_groups ||
10213
         hostname_component.has_regexp_groups ||
10214
         port_component.has_regexp_groups ||
10215
         pathname_component.has_regexp_groups ||
10216
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
10217
}
10218
10219
0
inline bool url_pattern_part::is_regexp() const noexcept {
10220
0
  return type == url_pattern_part_type::REGEXP;
10221
0
}
10222
10223
inline std::string_view url_pattern_compile_component_options::get_delimiter()
10224
0
    const {
10225
0
  if (delimiter) {
10226
0
    return {&delimiter.value(), 1};
10227
0
  }
10228
0
  return {};
10229
0
}
10230
10231
inline std::string_view url_pattern_compile_component_options::get_prefix()
10232
0
    const {
10233
0
  if (prefix) {
10234
0
    return {&prefix.value(), 1};
10235
0
  }
10236
0
  return {};
10237
0
}
10238
10239
template <url_pattern_regex::regex_concept regex_provider>
10240
template <url_pattern_encoding_callback F>
10241
tl::expected<url_pattern_component<regex_provider>, errors>
10242
url_pattern_component<regex_provider>::compile(
10243
    std::string_view input, F& encoding_callback,
10244
    url_pattern_compile_component_options& options) {
10245
  ada_log("url_pattern_component::compile input: ", input);
10246
  // Let part list be the result of running parse a pattern string given input,
10247
  // options, and encoding callback.
10248
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
10249
                                                             encoding_callback);
10250
10251
  if (!part_list) {
10252
    ada_log("parse_pattern_string failed");
10253
    return tl::unexpected(part_list.error());
10254
  }
10255
10256
  // Detect pattern type early to potentially skip expensive regex compilation
10257
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
10258
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
10259
10260
  url_pattern_component_type component_type =
10261
      url_pattern_component_type::REGEXP;
10262
  std::string exact_match_value{};
10263
10264
  if (part_list->empty()) {
10265
    component_type = url_pattern_component_type::EMPTY;
10266
  } else if (part_list->size() == 1) {
10267
    const auto& part = (*part_list)[0];
10268
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
10269
        part.modifier == url_pattern_part_modifier::none &&
10270
        !options.ignore_case) {
10271
      component_type = url_pattern_component_type::EXACT_MATCH;
10272
      exact_match_value = part.value;
10273
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
10274
               part.modifier == url_pattern_part_modifier::none &&
10275
               part.prefix.empty() && part.suffix.empty()) {
10276
      component_type = url_pattern_component_type::FULL_WILDCARD;
10277
    }
10278
  }
10279
10280
  // For simple patterns, skip regex generation and compilation entirely
10281
  if (component_type != url_pattern_component_type::REGEXP) {
10282
    auto pattern_string =
10283
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10284
    // For FULL_WILDCARD, we need the group name from
10285
    // generate_regular_expression
10286
    std::vector<std::string> name_list;
10287
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10288
        !part_list->empty()) {
10289
      name_list.push_back((*part_list)[0].name);
10290
    }
10291
    return url_pattern_component<regex_provider>(
10292
        std::move(pattern_string), typename regex_provider::regex_type{},
10293
        std::move(name_list), has_regexp_groups, component_type,
10294
        std::move(exact_match_value));
10295
  }
10296
10297
  // Generate regex for complex patterns
10298
  auto [regular_expression_string, name_list] =
10299
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10300
                                                                     options);
10301
  auto pattern_string =
10302
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10303
10304
  std::optional<typename regex_provider::regex_type> regular_expression =
10305
      regex_provider::create_instance(regular_expression_string,
10306
                                      options.ignore_case);
10307
  if (!regular_expression) {
10308
    return tl::unexpected(errors::type_error);
10309
  }
10310
10311
  return url_pattern_component<regex_provider>(
10312
      std::move(pattern_string), std::move(*regular_expression),
10313
      std::move(name_list), has_regexp_groups, component_type,
10314
      std::move(exact_match_value));
10315
}
10316
10317
template <url_pattern_regex::regex_concept regex_provider>
10318
bool url_pattern_component<regex_provider>::fast_test(
10319
    std::string_view input) const {
10320
  // Fast path for simple patterns - avoid regex evaluation
10321
  // Using if-else for better branch prediction on common cases
10322
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10323
    return true;
10324
  }
10325
  if (type == url_pattern_component_type::EXACT_MATCH) {
10326
    return input == exact_match_value;
10327
  }
10328
  if (type == url_pattern_component_type::EMPTY) {
10329
    return input.empty();
10330
  }
10331
  // type == REGEXP
10332
  return regex_provider::regex_match(input, regexp);
10333
}
10334
10335
template <url_pattern_regex::regex_concept regex_provider>
10336
std::optional<std::vector<std::optional<std::string>>>
10337
url_pattern_component<regex_provider>::fast_match(
10338
    std::string_view input) const {
10339
  // Handle each type directly without redundant checks
10340
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10341
    // FULL_WILDCARD always matches - capture the input (even if empty)
10342
    // If there's no group name, return empty groups
10343
    if (group_name_list.empty()) {
10344
      return std::vector<std::optional<std::string>>{};
10345
    }
10346
    // Capture the matched input (including empty strings)
10347
    return std::vector<std::optional<std::string>>{std::string(input)};
10348
  }
10349
  if (type == url_pattern_component_type::EXACT_MATCH) {
10350
    if (input == exact_match_value) {
10351
      return std::vector<std::optional<std::string>>{};
10352
    }
10353
    return std::nullopt;
10354
  }
10355
  if (type == url_pattern_component_type::EMPTY) {
10356
    if (input.empty()) {
10357
      return std::vector<std::optional<std::string>>{};
10358
    }
10359
    return std::nullopt;
10360
  }
10361
  // type == REGEXP - use regex
10362
  return regex_provider::regex_search(input, regexp);
10363
}
10364
10365
template <url_pattern_regex::regex_concept regex_provider>
10366
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10367
    const url_pattern_input& input, const std::string_view* base_url) {
10368
  // Return the result of match given this's associated URL pattern, input, and
10369
  // baseURL if given.
10370
  return match(input, base_url);
10371
}
10372
10373
template <url_pattern_regex::regex_concept regex_provider>
10374
bool url_pattern<regex_provider>::test_components(
10375
    std::string_view protocol, std::string_view username,
10376
    std::string_view password, std::string_view hostname, std::string_view port,
10377
    std::string_view pathname, std::string_view search,
10378
    std::string_view hash) const {
10379
  return protocol_component.fast_test(protocol) &&
10380
         username_component.fast_test(username) &&
10381
         password_component.fast_test(password) &&
10382
         hostname_component.fast_test(hostname) &&
10383
         port_component.fast_test(port) &&
10384
         pathname_component.fast_test(pathname) &&
10385
         search_component.fast_test(search) && hash_component.fast_test(hash);
10386
}
10387
10388
template <url_pattern_regex::regex_concept regex_provider>
10389
result<bool> url_pattern<regex_provider>::test(
10390
    const url_pattern_input& input, const std::string_view* base_url_string) {
10391
  // If input is a URLPatternInit
10392
  if (std::holds_alternative<url_pattern_init>(input)) {
10393
    if (base_url_string) {
10394
      return tl::unexpected(errors::type_error);
10395
    }
10396
10397
    std::string protocol{}, username{}, password{}, hostname{};
10398
    std::string port{}, pathname{}, search{}, hash{};
10399
10400
    auto apply_result = url_pattern_init::process(
10401
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10402
        protocol, username, password, hostname, port, pathname, search, hash);
10403
10404
    if (!apply_result) {
10405
      return false;
10406
    }
10407
10408
    std::string_view search_view = *apply_result->search;
10409
    if (search_view.starts_with("?")) {
10410
      search_view.remove_prefix(1);
10411
    }
10412
10413
    return test_components(*apply_result->protocol, *apply_result->username,
10414
                           *apply_result->password, *apply_result->hostname,
10415
                           *apply_result->port, *apply_result->pathname,
10416
                           search_view, *apply_result->hash);
10417
  }
10418
10419
  // URL string input path.
10420
  // Let baseURL be null. A default-constructed result holds a value (an
10421
  // empty URL), and a relative input would then resolve against it. Start
10422
  // from an error so "no base given" means no value.
10423
  result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10424
  if (base_url_string) {
10425
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10426
    if (!base_url) {
10427
      return false;
10428
    }
10429
  }
10430
10431
  auto url =
10432
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10433
                                 base_url.has_value() ? &*base_url : nullptr);
10434
  if (!url) {
10435
    return false;
10436
  }
10437
10438
  // Extract components as string_view
10439
  auto protocol_view = url->get_protocol();
10440
  if (protocol_view.ends_with(":")) {
10441
    protocol_view.remove_suffix(1);
10442
  }
10443
10444
  auto search_view = url->get_search();
10445
  if (search_view.starts_with("?")) {
10446
    search_view.remove_prefix(1);
10447
  }
10448
10449
  auto hash_view = url->get_hash();
10450
  if (hash_view.starts_with("#")) {
10451
    hash_view.remove_prefix(1);
10452
  }
10453
10454
  return test_components(protocol_view, url->get_username(),
10455
                         url->get_password(), url->get_hostname(),
10456
                         url->get_port(), url->get_pathname(), search_view,
10457
                         hash_view);
10458
}
10459
10460
template <url_pattern_regex::regex_concept regex_provider>
10461
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10462
    const url_pattern_input& input, const std::string_view* base_url_string) {
10463
  std::string protocol{};
10464
  std::string username{};
10465
  std::string password{};
10466
  std::string hostname{};
10467
  std::string port{};
10468
  std::string pathname{};
10469
  std::string search{};
10470
  std::string hash{};
10471
10472
  // Let inputs be an empty list.
10473
  // Append input to inputs.
10474
  std::vector inputs{input};
10475
10476
  // If input is a URLPatternInit then:
10477
  if (std::holds_alternative<url_pattern_init>(input)) {
10478
    ada_log(
10479
        "url_pattern::match called with url_pattern_init and base_url_string=",
10480
        base_url_string);
10481
    // If baseURLString was given, throw a TypeError.
10482
    if (base_url_string) {
10483
      ada_log("failed to match because base_url_string was given");
10484
      return tl::unexpected(errors::type_error);
10485
    }
10486
10487
    // Let applyResult be the result of process a URLPatternInit given input,
10488
    // "url", protocol, username, password, hostname, port, pathname, search,
10489
    // and hash.
10490
    auto apply_result = url_pattern_init::process(
10491
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10492
        protocol, username, password, hostname, port, pathname, search, hash);
10493
10494
    // If this throws an exception, catch it, and return null.
10495
    if (!apply_result.has_value()) {
10496
      ada_log("match returned std::nullopt because process threw");
10497
      return std::nullopt;
10498
    }
10499
10500
    // Set protocol to applyResult["protocol"].
10501
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10502
    protocol = std::move(apply_result->protocol.value());
10503
10504
    // Set username to applyResult["username"].
10505
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10506
    username = std::move(apply_result->username.value());
10507
10508
    // Set password to applyResult["password"].
10509
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10510
    password = std::move(apply_result->password.value());
10511
10512
    // Set hostname to applyResult["hostname"].
10513
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10514
    hostname = std::move(apply_result->hostname.value());
10515
10516
    // Set port to applyResult["port"].
10517
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10518
    port = std::move(apply_result->port.value());
10519
10520
    // Set pathname to applyResult["pathname"].
10521
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10522
    pathname = std::move(apply_result->pathname.value());
10523
10524
    // Set search to applyResult["search"].
10525
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10526
    if (apply_result->search->starts_with("?")) {
10527
      search = apply_result->search->substr(1);
10528
    } else {
10529
      search = std::move(apply_result->search.value());
10530
    }
10531
10532
    // Set hash to applyResult["hash"].
10533
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10534
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10535
    hash = std::move(apply_result->hash.value());
10536
  } else {
10537
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10538
10539
    // Let baseURL be null. A default-constructed result holds a value (an
10540
    // empty URL), and a relative input would then resolve against it. Start
10541
    // from an error so "no base given" means no value.
10542
    result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10543
10544
    // If baseURLString was given, then:
10545
    if (base_url_string) {
10546
      // Let baseURL be the result of parsing baseURLString.
10547
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10548
10549
      // If baseURL is failure, return null.
10550
      if (!base_url) {
10551
        ada_log("match returned std::nullopt because failed to parse base_url=",
10552
                *base_url_string);
10553
        return std::nullopt;
10554
      }
10555
10556
      // Append baseURLString to inputs.
10557
      inputs.emplace_back(*base_url_string);
10558
    }
10559
10560
    url_aggregator* base_url_value =
10561
        base_url.has_value() ? &*base_url : nullptr;
10562
10563
    // Set url to the result of parsing input given baseURL.
10564
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10565
                                          base_url_value);
10566
10567
    // If url is failure, return null.
10568
    if (!url) {
10569
      ada_log("match returned std::nullopt because url failed");
10570
      return std::nullopt;
10571
    }
10572
10573
    // Set protocol to url's scheme.
10574
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10575
    // is removed. Similar work was done on workerd:
10576
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10577
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10578
    // Set username to url's username.
10579
    username = url->get_username();
10580
    // Set password to url's password.
10581
    password = url->get_password();
10582
    // Set hostname to url's host, serialized, or the empty string if the value
10583
    // is null.
10584
    hostname = url->get_hostname();
10585
    // Set port to url's port, serialized, or the empty string if the value is
10586
    // null.
10587
    port = url->get_port();
10588
    // Set pathname to the result of URL path serializing url.
10589
    pathname = url->get_pathname();
10590
    // Set search to url's query or the empty string if the value is null.
10591
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10592
    // is removed. Similar work was done on workerd:
10593
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10594
    if (url->has_search()) {
10595
      auto view = url->get_search();
10596
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10597
    }
10598
    // Set hash to url's fragment or the empty string if the value is null.
10599
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10600
    // removed. Similar work was done on workerd:
10601
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10602
    if (url->has_hash()) {
10603
      auto view = url->get_hash();
10604
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10605
    }
10606
  }
10607
10608
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10609
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10610
10611
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10612
  // component's regular expression, protocol).
10613
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10614
  if (!protocol_exec_result) {
10615
    return std::nullopt;
10616
  }
10617
10618
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10619
  // component's regular expression, username).
10620
  auto username_exec_result = username_component.fast_match(username);
10621
  if (!username_exec_result) {
10622
    return std::nullopt;
10623
  }
10624
10625
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10626
  // component's regular expression, password).
10627
  auto password_exec_result = password_component.fast_match(password);
10628
  if (!password_exec_result) {
10629
    return std::nullopt;
10630
  }
10631
10632
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10633
  // component's regular expression, hostname).
10634
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10635
  if (!hostname_exec_result) {
10636
    return std::nullopt;
10637
  }
10638
10639
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10640
  // regular expression, port).
10641
  auto port_exec_result = port_component.fast_match(port);
10642
  if (!port_exec_result) {
10643
    return std::nullopt;
10644
  }
10645
10646
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10647
  // component's regular expression, pathname).
10648
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10649
  if (!pathname_exec_result) {
10650
    return std::nullopt;
10651
  }
10652
10653
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10654
  // regular expression, search).
10655
  auto search_exec_result = search_component.fast_match(search);
10656
  if (!search_exec_result) {
10657
    return std::nullopt;
10658
  }
10659
10660
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10661
  // regular expression, hash).
10662
  auto hash_exec_result = hash_component.fast_match(hash);
10663
  if (!hash_exec_result) {
10664
    return std::nullopt;
10665
  }
10666
10667
  // Let result be a new URLPatternResult.
10668
  auto result = url_pattern_result{};
10669
  // Set result["inputs"] to inputs.
10670
  result.inputs = std::move(inputs);
10671
  // Set result["protocol"] to the result of creating a component match result
10672
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10673
  result.protocol = protocol_component.create_component_match_result(
10674
      std::move(protocol), std::move(*protocol_exec_result));
10675
10676
  // Set result["username"] to the result of creating a component match result
10677
  // given urlPattern's username component, username, and usernameExecResult.
10678
  result.username = username_component.create_component_match_result(
10679
      std::move(username), std::move(*username_exec_result));
10680
10681
  // Set result["password"] to the result of creating a component match result
10682
  // given urlPattern's password component, password, and passwordExecResult.
10683
  result.password = password_component.create_component_match_result(
10684
      std::move(password), std::move(*password_exec_result));
10685
10686
  // Set result["hostname"] to the result of creating a component match result
10687
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10688
  result.hostname = hostname_component.create_component_match_result(
10689
      std::move(hostname), std::move(*hostname_exec_result));
10690
10691
  // Set result["port"] to the result of creating a component match result given
10692
  // urlPattern's port component, port, and portExecResult.
10693
  result.port = port_component.create_component_match_result(
10694
      std::move(port), std::move(*port_exec_result));
10695
10696
  // Set result["pathname"] to the result of creating a component match result
10697
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10698
  result.pathname = pathname_component.create_component_match_result(
10699
      std::move(pathname), std::move(*pathname_exec_result));
10700
10701
  // Set result["search"] to the result of creating a component match result
10702
  // given urlPattern's search component, search, and searchExecResult.
10703
  result.search = search_component.create_component_match_result(
10704
      std::move(search), std::move(*search_exec_result));
10705
10706
  // Set result["hash"] to the result of creating a component match result given
10707
  // urlPattern's hash component, hash, and hashExecResult.
10708
  result.hash = hash_component.create_component_match_result(
10709
      std::move(hash), std::move(*hash_exec_result));
10710
10711
  return result;
10712
}
10713
10714
}  // namespace ada
10715
#endif  // ADA_INCLUDE_URL_PATTERN
10716
#endif
10717
/* end file include/ada/url_pattern-inl.h */
10718
/* begin file include/ada/url_pattern_helpers-inl.h */
10719
/**
10720
 * @file url_pattern_helpers-inl.h
10721
 * @brief Declaration for the URLPattern helpers.
10722
 */
10723
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10724
#define ADA_URL_PATTERN_HELPERS_INL_H
10725
10726
#include <cstddef>
10727
#include <cstdint>
10728
#include <optional>
10729
#include <string>
10730
#include <string_view>
10731
#include <utility>
10732
#include <vector>
10733
10734
10735
#if ADA_INCLUDE_URL_PATTERN
10736
namespace ada::url_pattern_helpers {
10737
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10738
0
inline std::string to_string(token_type type) {
10739
0
  switch (type) {
10740
0
    case token_type::INVALID_CHAR:
10741
0
      return "INVALID_CHAR";
10742
0
    case token_type::OPEN:
10743
0
      return "OPEN";
10744
0
    case token_type::CLOSE:
10745
0
      return "CLOSE";
10746
0
    case token_type::REGEXP:
10747
0
      return "REGEXP";
10748
0
    case token_type::NAME:
10749
0
      return "NAME";
10750
0
    case token_type::CHAR:
10751
0
      return "CHAR";
10752
0
    case token_type::ESCAPED_CHAR:
10753
0
      return "ESCAPED_CHAR";
10754
0
    case token_type::OTHER_MODIFIER:
10755
0
      return "OTHER_MODIFIER";
10756
0
    case token_type::ASTERISK:
10757
0
      return "ASTERISK";
10758
0
    case token_type::END:
10759
0
      return "END";
10760
0
    default:
10761
0
      ada::unreachable();
10762
0
  }
10763
0
}
10764
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10765
10766
template <url_pattern_regex::regex_concept regex_provider>
10767
constexpr void constructor_string_parser<regex_provider>::rewind() {
10768
  // Set parser's token index to parser's component start.
10769
  token_index = component_start;
10770
  // Set parser's token increment to 0.
10771
  token_increment = 0;
10772
}
10773
10774
template <url_pattern_regex::regex_concept regex_provider>
10775
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10776
  // Return the result of running is a non-special pattern char given parser,
10777
  // parser's token index and "#".
10778
  return is_non_special_pattern_char(token_index, '#');
10779
}
10780
10781
template <url_pattern_regex::regex_concept regex_provider>
10782
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10783
  // If result of running is a non-special pattern char given parser, parser's
10784
  // token index and "?" is true, then return true.
10785
  if (is_non_special_pattern_char(token_index, '?')) {
10786
    return true;
10787
  }
10788
10789
  // If parser's token list[parser's token index]'s value is not "?", then
10790
  // return false.
10791
  if (token_list[token_index].value != "?") {
10792
    return false;
10793
  }
10794
10795
  // If previous index is less than 0, then return true.
10796
  if (token_index == 0) return true;
10797
  // Let previous index be parser's token index - 1.
10798
  auto previous_index = token_index - 1;
10799
  // Let previous token be the result of running get a safe token given parser
10800
  // and previous index.
10801
  auto previous_token = get_safe_token(previous_index);
10802
  ADA_ASSERT_TRUE(previous_token);
10803
  // If any of the following are true, then return false:
10804
  // - previous token's type is "name".
10805
  // - previous token's type is "regexp".
10806
  // - previous token's type is "close".
10807
  // - previous token's type is "asterisk".
10808
  return !(previous_token->type == token_type::NAME ||
10809
           previous_token->type == token_type::REGEXP ||
10810
           previous_token->type == token_type::CLOSE ||
10811
           previous_token->type == token_type::ASTERISK);
10812
}
10813
10814
template <url_pattern_regex::regex_concept regex_provider>
10815
constexpr bool
10816
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10817
    size_t index, uint32_t value) const {
10818
  // Let token be the result of running get a safe token given parser and index.
10819
  auto token = get_safe_token(index);
10820
  ADA_ASSERT_TRUE(token);
10821
10822
  // If token's value is not value, then return false.
10823
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10824
  // string.
10825
  if (!token->value.empty() &&
10826
      static_cast<uint32_t>(token->value[0]) != value) {
10827
    return false;
10828
  }
10829
10830
  // If any of the following are true:
10831
  // - token's type is "char";
10832
  // - token's type is "escaped-char"; or
10833
  // - token's type is "invalid-char",
10834
  // - then return true.
10835
  return token->type == token_type::CHAR ||
10836
         token->type == token_type::ESCAPED_CHAR ||
10837
         token->type == token_type::INVALID_CHAR;
10838
}
10839
10840
template <url_pattern_regex::regex_concept regex_provider>
10841
constexpr const token*
10842
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10843
  // If index is less than parser's token list's size, then return parser's
10844
  // token list[index].
10845
  if (index < token_list.size()) [[likely]] {
10846
    return &token_list[index];
10847
  }
10848
10849
  // Assert: parser's token list's size is greater than or equal to 1.
10850
  ADA_ASSERT_TRUE(!token_list.empty());
10851
10852
  // Let token be parser's token list[last index].
10853
  // Assert: token's type is "end".
10854
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10855
10856
  // Return token.
10857
  return &token_list.back();
10858
}
10859
10860
template <url_pattern_regex::regex_concept regex_provider>
10861
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10862
    const {
10863
  // If parser's token list[parser's token index]'s type is "open", then return
10864
  // true.
10865
  return token_list[token_index].type == token_type::OPEN;
10866
}
10867
10868
template <url_pattern_regex::regex_concept regex_provider>
10869
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10870
    const {
10871
  // If parser's token list[parser's token index]'s type is "close", then return
10872
  // true.
10873
  return token_list[token_index].type == token_type::CLOSE;
10874
}
10875
10876
template <url_pattern_regex::regex_concept regex_provider>
10877
constexpr bool
10878
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10879
  // If the result of running is a non-special pattern char given parser,
10880
  // parser's token index + 1, and "/" is false, then return false.
10881
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10882
    return false;
10883
  }
10884
  // If the result of running is a non-special pattern char given parser,
10885
  // parser's token index + 2, and "/" is false, then return false.
10886
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10887
    return false;
10888
  }
10889
  return true;
10890
}
10891
10892
template <url_pattern_regex::regex_concept regex_provider>
10893
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10894
    const {
10895
  // Return the result of running is a non-special pattern char given parser,
10896
  // parser's token index, and ":".
10897
  return is_non_special_pattern_char(token_index, ':');
10898
}
10899
10900
template <url_pattern_regex::regex_concept regex_provider>
10901
void constructor_string_parser<regex_provider>::change_state(State new_state,
10902
                                                             size_t skip) {
10903
  // If parser's state is not "init", not "authority", and not "done", then set
10904
  // parser's result[parser's state] to the result of running make a component
10905
  // string given parser.
10906
  if (state != State::INIT && state != State::AUTHORITY &&
10907
      state != State::DONE) {
10908
    auto value = make_component_string();
10909
    // TODO: Simplify this.
10910
    switch (state) {
10911
      case State::PROTOCOL: {
10912
        result.protocol = value;
10913
        break;
10914
      }
10915
      case State::USERNAME: {
10916
        result.username = value;
10917
        break;
10918
      }
10919
      case State::PASSWORD: {
10920
        result.password = value;
10921
        break;
10922
      }
10923
      case State::HOSTNAME: {
10924
        result.hostname = value;
10925
        break;
10926
      }
10927
      case State::PORT: {
10928
        result.port = value;
10929
        break;
10930
      }
10931
      case State::PATHNAME: {
10932
        result.pathname = value;
10933
        break;
10934
      }
10935
      case State::SEARCH: {
10936
        result.search = value;
10937
        break;
10938
      }
10939
      case State::HASH: {
10940
        result.hash = value;
10941
        break;
10942
      }
10943
      default:
10944
        ada::unreachable();
10945
    }
10946
  }
10947
10948
  // If parser's state is not "init" and new state is not "done", then:
10949
  if (state != State::INIT && new_state != State::DONE) {
10950
    // If parser's state is "protocol", "authority", "username", or "password";
10951
    // new state is "port", "pathname", "search", or "hash"; and parser's
10952
    // result["hostname"] does not exist, then set parser's result["hostname"]
10953
    // to the empty string.
10954
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10955
         state == State::USERNAME || state == State::PASSWORD) &&
10956
        (new_state == State::PORT || new_state == State::PATHNAME ||
10957
         new_state == State::SEARCH || new_state == State::HASH) &&
10958
        !result.hostname)
10959
      result.hostname = "";
10960
  }
10961
10962
  // If parser's state is "protocol", "authority", "username", "password",
10963
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10964
  // result["pathname"] does not exist, then:
10965
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10966
       state == State::USERNAME || state == State::PASSWORD ||
10967
       state == State::HOSTNAME || state == State::PORT) &&
10968
      (new_state == State::SEARCH || new_state == State::HASH) &&
10969
      !result.pathname) {
10970
    if (protocol_matches_a_special_scheme_flag) {
10971
      result.pathname = "/";
10972
    } else {
10973
      // Otherwise, set parser's result["pathname"] to the empty string.
10974
      result.pathname = "";
10975
    }
10976
  }
10977
10978
  // If parser's state is "protocol", "authority", "username", "password",
10979
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10980
  // result["search"] does not exist, then set parser's result["search"] to
10981
  // the empty string.
10982
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10983
       state == State::USERNAME || state == State::PASSWORD ||
10984
       state == State::HOSTNAME || state == State::PORT ||
10985
       state == State::PATHNAME) &&
10986
      new_state == State::HASH && !result.search) {
10987
    result.search = "";
10988
  }
10989
10990
  // Set parser's state to new state.
10991
  state = new_state;
10992
  // Increment parser's token index by skip.
10993
  token_index += skip;
10994
  // Set parser's component start to parser's token index.
10995
  component_start = token_index;
10996
  // Set parser's token increment to 0.
10997
  token_increment = 0;
10998
}
10999
11000
template <url_pattern_regex::regex_concept regex_provider>
11001
std::string constructor_string_parser<regex_provider>::make_component_string() {
11002
  // Assert: parser's token index is less than parser's token list's size.
11003
  ADA_ASSERT_TRUE(token_index < token_list.size());
11004
11005
  // Let token be parser's token list[parser's token index].
11006
  // Let end index be token's index.
11007
  const auto end_index = token_list[token_index].index;
11008
  // Let component start token be the result of running get a safe token given
11009
  // parser and parser's component start.
11010
  const auto component_start_token = get_safe_token(component_start);
11011
  ADA_ASSERT_TRUE(component_start_token);
11012
  // Let component start input index be component start token's index.
11013
  const auto component_start_input_index = component_start_token->index;
11014
  // Return the code point substring from component start input index to end
11015
  // index within parser's input.
11016
  return std::string(input.substr(component_start_input_index,
11017
                                  end_index - component_start_input_index));
11018
}
11019
11020
template <url_pattern_regex::regex_concept regex_provider>
11021
constexpr bool
11022
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
11023
  // Return the result of running is a non-special pattern char given parser,
11024
  // parser's token index, and "@".
11025
  return is_non_special_pattern_char(token_index, '@');
11026
}
11027
11028
template <url_pattern_regex::regex_concept regex_provider>
11029
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
11030
    const {
11031
  // Return the result of running is a non-special pattern char given parser,
11032
  // parser's token index, and "/".
11033
  return is_non_special_pattern_char(token_index, '/');
11034
}
11035
11036
template <url_pattern_regex::regex_concept regex_provider>
11037
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
11038
    const {
11039
  // Return the result of running is a non-special pattern char given parser,
11040
  // parser's token index, and ":".
11041
  return is_non_special_pattern_char(token_index, ':');
11042
}
11043
11044
template <url_pattern_regex::regex_concept regex_provider>
11045
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
11046
    const {
11047
  // Return the result of running is a non-special pattern char given parser,
11048
  // parser's token index, and "[".
11049
  return is_non_special_pattern_char(token_index, '[');
11050
}
11051
11052
template <url_pattern_regex::regex_concept regex_provider>
11053
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
11054
    const {
11055
  // Return the result of running is a non-special pattern char given parser,
11056
  // parser's token index, and "]".
11057
  return is_non_special_pattern_char(token_index, ']');
11058
}
11059
11060
template <url_pattern_regex::regex_concept regex_provider>
11061
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
11062
    const {
11063
  // Return the result of running is a non-special pattern char given parser,
11064
  // parser's token index, and ":".
11065
  return is_non_special_pattern_char(token_index, ':');
11066
}
11067
11068
0
constexpr void Tokenizer::get_next_code_point() {
11069
0
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
11070
0
  ADA_ASSERT_TRUE(next_index < input.size());
11071
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
11072
  // invalid, return the offending byte as the code point, and advance by one
11073
  // to guarantee forward progress.
11074
0
  invalid_code_point = false;
11075
0
  code_point = 0;
11076
0
  size_t number_bytes = 0;
11077
0
  const size_t initial_index = next_index;
11078
0
  unsigned char first_byte = input[next_index];
11079
11080
0
  if ((first_byte & 0x80) == 0) {
11081
    // 1-byte character (ASCII)
11082
0
    next_index++;
11083
0
    code_point = first_byte;
11084
0
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
11085
0
            uint32_t(code_point));
11086
0
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11087
0
            " input.size()=", input.size());
11088
0
    return;
11089
0
  }
11090
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
11091
0
          uint32_t(first_byte));
11092
0
  if ((first_byte & 0xE0) == 0xC0) {
11093
0
    code_point = first_byte & 0x1F;
11094
0
    number_bytes = 2;
11095
0
    ada_log("Tokenizer::get_next_code_point two bytes");
11096
0
  } else if ((first_byte & 0xF0) == 0xE0) {
11097
0
    code_point = first_byte & 0x0F;
11098
0
    number_bytes = 3;
11099
0
    ada_log("Tokenizer::get_next_code_point three bytes");
11100
0
  } else if ((first_byte & 0xF8) == 0xF0) {
11101
0
    code_point = first_byte & 0x07;
11102
0
    number_bytes = 4;
11103
0
    ada_log("Tokenizer::get_next_code_point four bytes");
11104
0
  }
11105
11106
  // Invalid leading bytes that still match a multi-byte prefix.
11107
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
11108
0
      (number_bytes == 4 && first_byte > 0xF4)) {
11109
0
    invalid_code_point = true;
11110
0
    code_point = first_byte;
11111
0
    next_index = initial_index + 1;
11112
0
    return;
11113
0
  }
11114
11115
  // Invalid leading byte (e.g., continuation byte outside a sequence).
11116
0
  if (number_bytes == 0) {
11117
0
    invalid_code_point = true;
11118
0
    code_point = first_byte;
11119
0
    next_index = initial_index + 1;
11120
0
    return;
11121
0
  }
11122
11123
  // Truncated UTF-8 sequence.
11124
0
  if (number_bytes + next_index > input.size()) {
11125
0
    invalid_code_point = true;
11126
0
    code_point = first_byte;
11127
0
    next_index = initial_index + 1;
11128
0
    return;
11129
0
  }
11130
11131
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
11132
0
    unsigned char byte = input[i];
11133
0
    if ((byte & 0xC0) != 0x80) {
11134
0
      invalid_code_point = true;
11135
0
      code_point = first_byte;
11136
0
      next_index = initial_index + 1;
11137
0
      return;
11138
0
    }
11139
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
11140
0
    code_point = (code_point << 6) | (byte & 0x3F);
11141
0
  }
11142
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
11143
0
          uint32_t(code_point));
11144
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11145
0
          " input.size()=", input.size());
11146
0
  next_index += number_bytes;
11147
0
}
11148
11149
0
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
11150
0
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
11151
0
          new_index);
11152
  // Set tokenizer's next index to index.
11153
0
  next_index = new_index;
11154
  // Run get the next code point given tokenizer.
11155
0
  get_next_code_point();
11156
0
}
11157
11158
inline void Tokenizer::add_token(token_type type, size_t next_position,
11159
0
                                 size_t value_position, size_t value_length) {
11160
0
  ada_log("Tokenizer::add_token called with type=", to_string(type),
11161
0
          " next_position=", next_position, " value_position=", value_position);
11162
0
  ADA_ASSERT_TRUE(next_position >= value_position);
11163
11164
  // Let token be a new token.
11165
  // Set token's type to type.
11166
  // Set token's index to tokenizer's index.
11167
  // Set token's value to the code point substring from value position with
11168
  // length value length within tokenizer's input.
11169
  // Append token to the back of tokenizer's token list.
11170
0
  token_list.emplace_back(type, index,
11171
0
                          input.substr(value_position, value_length));
11172
  // Set tokenizer's index to next position.
11173
0
  index = next_position;
11174
0
}
11175
11176
inline void Tokenizer::add_token_with_default_length(token_type type,
11177
                                                     size_t next_position,
11178
0
                                                     size_t value_position) {
11179
  // Let computed length be next position - value position.
11180
0
  auto computed_length = next_position - value_position;
11181
  // Run add a token given tokenizer, type, next position, value position, and
11182
  // computed length.
11183
0
  add_token(type, next_position, value_position, computed_length);
11184
0
}
11185
11186
0
inline void Tokenizer::add_token_with_defaults(token_type type) {
11187
0
  ada_log("Tokenizer::add_token_with_defaults called with type=",
11188
0
          to_string(type));
11189
  // Run add a token with default length given tokenizer, type, tokenizer's next
11190
  // index, and tokenizer's index.
11191
0
  add_token_with_default_length(type, next_index, index);
11192
0
}
11193
11194
inline ada_warn_unused std::optional<errors>
11195
Tokenizer::process_tokenizing_error(size_t next_position,
11196
0
                                    size_t value_position) {
11197
  // If tokenizer's policy is "strict", then throw a TypeError.
11198
0
  if (policy == token_policy::strict) {
11199
0
    ada_log("process_tokenizing_error failed with next_position=",
11200
0
            next_position, " value_position=", value_position);
11201
0
    return errors::type_error;
11202
0
  }
11203
  // Assert: tokenizer's policy is "lenient".
11204
0
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
11205
  // Run add a token with default length given tokenizer, "invalid-char", next
11206
  // position, and value position.
11207
0
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
11208
0
                                value_position);
11209
0
  return std::nullopt;
11210
0
}
11211
11212
template <url_pattern_encoding_callback F>
11213
0
token* url_pattern_parser<F>::try_consume_modifier_token() {
11214
  // Let token be the result of running try to consume a token given parser and
11215
  // "other-modifier".
11216
0
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
11217
  // If token is not null, then return token.
11218
0
  if (token) return token;
11219
  // Set token to the result of running try to consume a token given parser and
11220
  // "asterisk".
11221
  // Return token.
11222
0
  return try_consume_token(token_type::ASTERISK);
11223
0
}
11224
11225
template <url_pattern_encoding_callback F>
11226
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
11227
0
    const token* name_token) {
11228
  // Let token be the result of running try to consume a token given parser and
11229
  // "regexp".
11230
0
  auto token = try_consume_token(token_type::REGEXP);
11231
  // If name token is null and token is null, then set token to the result of
11232
  // running try to consume a token given parser and "asterisk".
11233
0
  if (!name_token && !token) {
11234
0
    token = try_consume_token(token_type::ASTERISK);
11235
0
  }
11236
  // Return token.
11237
0
  return token;
11238
0
}
11239
11240
template <url_pattern_encoding_callback F>
11241
0
token* url_pattern_parser<F>::try_consume_token(token_type type) {
11242
0
  ada_log("url_pattern_parser::try_consume_token called with type=",
11243
0
          to_string(type));
11244
  // Assert: parser's index is less than parser's token list size.
11245
0
  ADA_ASSERT_TRUE(index < tokens.size());
11246
  // Let next token be parser's token list[parser's index].
11247
0
  auto& next_token = tokens[index];
11248
  // If next token's type is not type return null.
11249
0
  if (next_token.type != type) return nullptr;
11250
  // Increase parser's index by 1.
11251
0
  index++;
11252
  // Return next token.
11253
0
  return &next_token;
11254
0
}
11255
11256
template <url_pattern_encoding_callback F>
11257
0
std::string url_pattern_parser<F>::consume_text() {
11258
  // Let result be the empty string.
11259
0
  std::string result{};
11260
  // While true:
11261
0
  while (true) {
11262
    // Let token be the result of running try to consume a token given parser
11263
    // and "char".
11264
0
    auto token = try_consume_token(token_type::CHAR);
11265
    // If token is null, then set token to the result of running try to consume
11266
    // a token given parser and "escaped-char".
11267
0
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
11268
    // If token is null, then break.
11269
0
    if (!token) break;
11270
    // Append token's value to the end of result.
11271
0
    result.append(token->value);
11272
0
  }
11273
  // Return result.
11274
0
  return result;
11275
0
}
11276
11277
template <url_pattern_encoding_callback F>
11278
0
bool url_pattern_parser<F>::consume_required_token(token_type type) {
11279
0
  ada_log("url_pattern_parser::consume_required_token called with type=",
11280
0
          to_string(type));
11281
  // Let result be the result of running try to consume a token given parser and
11282
  // type.
11283
0
  return try_consume_token(type) != nullptr;
11284
0
}
11285
11286
template <url_pattern_encoding_callback F>
11287
std::optional<errors>
11288
0
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
11289
  // If parser's pending fixed value is the empty string, then return.
11290
0
  if (pending_fixed_value.empty()) {
11291
0
    ada_log("pending_fixed_value is empty");
11292
0
    return std::nullopt;
11293
0
  }
11294
  // Let encoded value be the result of running parser's encoding callback given
11295
  // parser's pending fixed value.
11296
0
  auto encoded_value = encoding_callback(pending_fixed_value);
11297
0
  if (!encoded_value) {
11298
0
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11299
0
    return encoded_value.error();
11300
0
  }
11301
  // Set parser's pending fixed value to the empty string.
11302
0
  pending_fixed_value.clear();
11303
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11304
  // and modifier is "none".
11305
  // Append part to parser's part list.
11306
0
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11307
0
                     std::move(*encoded_value),
11308
0
                     url_pattern_part_modifier::none);
11309
0
  return std::nullopt;
11310
0
}
11311
11312
template <url_pattern_encoding_callback F>
11313
std::optional<errors> url_pattern_parser<F>::add_part(
11314
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11315
0
    std::string_view suffix, token* modifier_token) {
11316
  // Let modifier be "none".
11317
0
  auto modifier = url_pattern_part_modifier::none;
11318
  // If modifier token is not null:
11319
0
  if (modifier_token) {
11320
    // If modifier token's value is "?" then set modifier to "optional".
11321
0
    if (modifier_token->value == "?") {
11322
0
      modifier = url_pattern_part_modifier::optional;
11323
0
    } else if (modifier_token->value == "*") {
11324
      // Otherwise if modifier token's value is "*" then set modifier to
11325
      // "zero-or-more".
11326
0
      modifier = url_pattern_part_modifier::zero_or_more;
11327
0
    } else if (modifier_token->value == "+") {
11328
      // Otherwise if modifier token's value is "+" then set modifier to
11329
      // "one-or-more".
11330
0
      modifier = url_pattern_part_modifier::one_or_more;
11331
0
    }
11332
0
  }
11333
  // If name token is null and regexp or wildcard token is null and modifier
11334
  // is "none":
11335
0
  if (!name_token && !regexp_or_wildcard_token &&
11336
0
      modifier == url_pattern_part_modifier::none) {
11337
    // Append prefix to the end of parser's pending fixed value.
11338
0
    pending_fixed_value.append(prefix);
11339
0
    return std::nullopt;
11340
0
  }
11341
  // Run maybe add a part from the pending fixed value given parser.
11342
0
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11343
0
    return *error;
11344
0
  }
11345
  // If name token is null and regexp or wildcard token is null:
11346
0
  if (!name_token && !regexp_or_wildcard_token) {
11347
    // Assert: suffix is the empty string.
11348
0
    ADA_ASSERT_TRUE(suffix.empty());
11349
    // If prefix is the empty string, then return.
11350
0
    if (prefix.empty()) return std::nullopt;
11351
    // Let encoded value be the result of running parser's encoding callback
11352
    // given prefix.
11353
0
    auto encoded_value = encoding_callback(prefix);
11354
0
    if (!encoded_value) {
11355
0
      return encoded_value.error();
11356
0
    }
11357
    // Let part be a new part whose type is "fixed-text", value is encoded
11358
    // value, and modifier is modifier.
11359
    // Append part to parser's part list.
11360
0
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11361
0
                       std::move(*encoded_value), modifier);
11362
0
    return std::nullopt;
11363
0
  }
11364
  // Let regexp value be the empty string.
11365
0
  std::string regexp_value{};
11366
  // If regexp or wildcard token is null, then set regexp value to parser's
11367
  // segment wildcard regexp.
11368
0
  if (!regexp_or_wildcard_token) {
11369
0
    regexp_value = segment_wildcard_regexp;
11370
0
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11371
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11372
    // regexp value to the full wildcard regexp value.
11373
0
    regexp_value = ".*";
11374
0
  } else {
11375
    // Otherwise set regexp value to regexp or wildcard token's value.
11376
0
    regexp_value = regexp_or_wildcard_token->value;
11377
0
  }
11378
  // Let type be "regexp".
11379
0
  auto type = url_pattern_part_type::REGEXP;
11380
  // If regexp value is parser's segment wildcard regexp:
11381
0
  if (regexp_value == segment_wildcard_regexp) {
11382
    // Set type to "segment-wildcard".
11383
0
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11384
    // Set regexp value to the empty string.
11385
0
    regexp_value.clear();
11386
0
  } else if (regexp_value == ".*") {
11387
    // Otherwise if regexp value is the full wildcard regexp value:
11388
    // Set type to "full-wildcard".
11389
0
    type = url_pattern_part_type::FULL_WILDCARD;
11390
    // Set regexp value to the empty string.
11391
0
    regexp_value.clear();
11392
0
  }
11393
  // Let name be the empty string.
11394
0
  std::string name{};
11395
  // If name token is not null, then set name to name token's value.
11396
0
  if (name_token) {
11397
0
    name = name_token->value;
11398
0
  } else if (regexp_or_wildcard_token != nullptr) {
11399
    // Otherwise if regexp or wildcard token is not null:
11400
    // Set name to parser's next numeric name, serialized.
11401
0
    name = std::to_string(next_numeric_name);
11402
    // Increment parser's next numeric name by 1.
11403
0
    next_numeric_name++;
11404
0
  }
11405
  // If the result of running is a duplicate name given parser and name is
11406
  // true, then throw a TypeError. Tracked in a set so this stays O(1): a
11407
  // linear scan of the part list here is O(n^2) over a pattern string with n
11408
  // named parts (e.g. "/*" repeated), an algorithmic-complexity DoS.
11409
0
  if (!name.empty() && !seen_names.insert(name).second) {
11410
0
    return errors::type_error;
11411
0
  }
11412
  // Let encoded prefix be the result of running parser's encoding callback
11413
  // given prefix.
11414
0
  auto encoded_prefix = encoding_callback(prefix);
11415
0
  if (!encoded_prefix) return encoded_prefix.error();
11416
  // Let encoded suffix be the result of running parser's encoding callback
11417
  // given suffix.
11418
0
  auto encoded_suffix = encoding_callback(suffix);
11419
0
  if (!encoded_suffix) return encoded_suffix.error();
11420
  // Let part be a new part whose type is type, value is regexp value,
11421
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11422
  // is encoded suffix.
11423
  // Append part to parser's part list.
11424
0
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11425
0
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11426
0
  return std::nullopt;
11427
0
}
11428
11429
template <url_pattern_encoding_callback F>
11430
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11431
    std::string_view input, url_pattern_compile_component_options& options,
11432
0
    F& encoding_callback) {
11433
0
  ada_log("parse_pattern_string input=", input);
11434
  // Let parser be a new pattern parser whose encoding callback is encoding
11435
  // callback and segment wildcard regexp is the result of running generate a
11436
  // segment wildcard regexp given options.
11437
0
  auto parser = url_pattern_parser<F>(
11438
0
      encoding_callback, generate_segment_wildcard_regexp(options));
11439
  // Set parser's token list to the result of running tokenize given input and
11440
  // "strict".
11441
0
  auto tokenize_result = tokenize(input, token_policy::strict);
11442
0
  if (!tokenize_result) {
11443
0
    ada_log("parse_pattern_string tokenize failed");
11444
0
    return tl::unexpected(tokenize_result.error());
11445
0
  }
11446
0
  parser.tokens = std::move(*tokenize_result);
11447
11448
  // While parser's index is less than parser's token list's size:
11449
0
  while (parser.can_continue()) {
11450
    // Let char token be the result of running try to consume a token given
11451
    // parser and "char".
11452
0
    auto char_token = parser.try_consume_token(token_type::CHAR);
11453
    // Let name token be the result of running try to consume a token given
11454
    // parser and "name".
11455
0
    auto name_token = parser.try_consume_token(token_type::NAME);
11456
    // Let regexp or wildcard token be the result of running try to consume a
11457
    // regexp or wildcard token given parser and name token.
11458
0
    auto regexp_or_wildcard_token =
11459
0
        parser.try_consume_regexp_or_wildcard_token(name_token);
11460
    // If name token is not null or regexp or wildcard token is not null:
11461
0
    if (name_token || regexp_or_wildcard_token) {
11462
      // Let prefix be the empty string.
11463
0
      std::string prefix{};
11464
      // If char token is not null then set prefix to char token's value.
11465
0
      if (char_token) prefix = char_token->value;
11466
      // If prefix is not the empty string and not options's prefix code point:
11467
0
      if (!prefix.empty() && prefix != options.get_prefix()) {
11468
        // Append prefix to the end of parser's pending fixed value.
11469
0
        parser.pending_fixed_value.append(prefix);
11470
        // Set prefix to the empty string.
11471
0
        prefix.clear();
11472
0
      }
11473
      // Run maybe add a part from the pending fixed value given parser.
11474
0
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11475
0
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11476
0
        return tl::unexpected(*error);
11477
0
      }
11478
      // Let modifier token be the result of running try to consume a modifier
11479
      // token given parser.
11480
0
      auto modifier_token = parser.try_consume_modifier_token();
11481
      // Run add a part given parser, prefix, name token, regexp or wildcard
11482
      // token, the empty string, and modifier token.
11483
0
      if (auto error =
11484
0
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11485
0
                              modifier_token)) {
11486
0
        ada_log("parser.add_part failed");
11487
0
        return tl::unexpected(*error);
11488
0
      }
11489
      // Continue.
11490
0
      continue;
11491
0
    }
11492
11493
    // Let fixed token be char token.
11494
0
    auto fixed_token = char_token;
11495
    // If fixed token is null, then set fixed token to the result of running try
11496
    // to consume a token given parser and "escaped-char".
11497
0
    if (!fixed_token)
11498
0
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11499
    // If fixed token is not null:
11500
0
    if (fixed_token) {
11501
      // Append fixed token's value to parser's pending fixed value.
11502
0
      parser.pending_fixed_value.append(fixed_token->value);
11503
      // Continue.
11504
0
      continue;
11505
0
    }
11506
    // Let open token be the result of running try to consume a token given
11507
    // parser and "open".
11508
0
    auto open_token = parser.try_consume_token(token_type::OPEN);
11509
    // If open token is not null:
11510
0
    if (open_token) {
11511
      // Set prefix be the result of running consume text given parser.
11512
0
      auto prefix_ = parser.consume_text();
11513
      // Set name token to the result of running try to consume a token given
11514
      // parser and "name".
11515
0
      name_token = parser.try_consume_token(token_type::NAME);
11516
      // Set regexp or wildcard token to the result of running try to consume a
11517
      // regexp or wildcard token given parser and name token.
11518
0
      regexp_or_wildcard_token =
11519
0
          parser.try_consume_regexp_or_wildcard_token(name_token);
11520
      // Let suffix be the result of running consume text given parser.
11521
0
      auto suffix_ = parser.consume_text();
11522
      // Run consume a required token given parser and "close".
11523
0
      if (!parser.consume_required_token(token_type::CLOSE)) {
11524
0
        ada_log("parser.consume_required_token failed");
11525
0
        return tl::unexpected(errors::type_error);
11526
0
      }
11527
      // Set modifier token to the result of running try to consume a modifier
11528
      // token given parser.
11529
0
      auto modifier_token = parser.try_consume_modifier_token();
11530
      // Run add a part given parser, prefix, name token, regexp or wildcard
11531
      // token, suffix, and modifier token.
11532
0
      if (auto error =
11533
0
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11534
0
                              suffix_, modifier_token)) {
11535
0
        return tl::unexpected(*error);
11536
0
      }
11537
      // Continue.
11538
0
      continue;
11539
0
    }
11540
    // Run maybe add a part from the pending fixed value given parser.
11541
0
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11542
0
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11543
0
      return tl::unexpected(*error);
11544
0
    }
11545
    // Run consume a required token given parser and "end".
11546
0
    if (!parser.consume_required_token(token_type::END)) {
11547
0
      return tl::unexpected(errors::type_error);
11548
0
    }
11549
0
  }
11550
0
  ada_log("parser.parts size is: ", parser.parts.size());
11551
  // Return parser's part list.
11552
0
  return parser.parts;
11553
0
}
11554
11555
template <url_pattern_regex::regex_concept regex_provider>
11556
bool protocol_component_matches_special_scheme(
11557
    url_pattern_component<regex_provider>& component) {
11558
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11559
  switch (component.type) {
11560
    case url_pattern_component_type::EMPTY:
11561
      // Empty pattern can't match any special scheme
11562
      return false;
11563
    case url_pattern_component_type::EXACT_MATCH:
11564
      // Direct string comparison for exact match patterns
11565
      return component.exact_match_value == "http" ||
11566
             component.exact_match_value == "https" ||
11567
             component.exact_match_value == "ws" ||
11568
             component.exact_match_value == "wss" ||
11569
             component.exact_match_value == "ftp" ||
11570
             component.exact_match_value == "file";
11571
    case url_pattern_component_type::FULL_WILDCARD:
11572
      // Full wildcard matches everything including special schemes
11573
      return true;
11574
    case url_pattern_component_type::REGEXP:
11575
      // Fall back to regex matching for complex patterns
11576
      auto& regex = component.regexp;
11577
      return regex_provider::regex_match("http", regex) ||
11578
             regex_provider::regex_match("https", regex) ||
11579
             regex_provider::regex_match("ws", regex) ||
11580
             regex_provider::regex_match("wss", regex) ||
11581
             regex_provider::regex_match("ftp", regex) ||
11582
             regex_provider::regex_match("file", regex);
11583
  }
11584
  ada::unreachable();
11585
}
11586
11587
template <url_pattern_regex::regex_concept regex_provider>
11588
inline std::optional<errors> constructor_string_parser<
11589
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11590
  ada_log(
11591
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11592
      "flag");
11593
  // Let protocol string be the result of running make a component string given
11594
  // parser.
11595
  auto protocol_string = make_component_string();
11596
  // Let protocol component be the result of compiling a component given
11597
  // protocol string, canonicalize a protocol, and default options.
11598
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11599
      protocol_string, canonicalize_protocol,
11600
      url_pattern_compile_component_options::DEFAULT);
11601
  if (!protocol_component) {
11602
    ada_log("url_pattern_component::compile failed for protocol_string ",
11603
            protocol_string);
11604
    return protocol_component.error();
11605
  }
11606
  // If the result of running protocol component matches a special scheme given
11607
  // protocol component is true, then set parser's protocol matches a special
11608
  // scheme flag to true.
11609
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11610
    protocol_matches_a_special_scheme_flag = true;
11611
  }
11612
  return std::nullopt;
11613
}
11614
11615
template <url_pattern_regex::regex_concept regex_provider>
11616
tl::expected<url_pattern_init, errors>
11617
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11618
  ada_log("constructor_string_parser::parse input=", input);
11619
  // Let parser be a new constructor string parser whose input is input and
11620
  // token list is the result of running tokenize given input and "lenient".
11621
  auto token_list = tokenize(input, token_policy::lenient);
11622
  if (!token_list) {
11623
    return tl::unexpected(token_list.error());
11624
  }
11625
  auto parser = constructor_string_parser(input, std::move(*token_list));
11626
11627
  // While parser's token index is less than parser's token list size:
11628
  while (parser.token_index < parser.token_list.size()) {
11629
    // Set parser's token increment to 1.
11630
    parser.token_increment = 1;
11631
11632
    // If parser's token list[parser's token index]'s type is "end" then:
11633
    if (parser.token_list[parser.token_index].type == token_type::END) {
11634
      // If parser's state is "init":
11635
      if (parser.state == State::INIT) {
11636
        // Run rewind given parser.
11637
        parser.rewind();
11638
        // If the result of running is a hash prefix given parser is true, then
11639
        // run change state given parser, "hash" and 1.
11640
        if (parser.is_hash_prefix()) {
11641
          parser.change_state(State::HASH, 1);
11642
        } else if (parser.is_search_prefix()) {
11643
          // Otherwise if the result of running is a search prefix given parser
11644
          // is true: Run change state given parser, "search" and 1.
11645
          parser.change_state(State::SEARCH, 1);
11646
        } else {
11647
          // Run change state given parser, "pathname" and 0.
11648
          parser.change_state(State::PATHNAME, 0);
11649
        }
11650
        // Increment parser's token index by parser's token increment.
11651
        parser.token_index += parser.token_increment;
11652
        // Continue.
11653
        continue;
11654
      }
11655
11656
      if (parser.state == State::AUTHORITY) {
11657
        // If parser's state is "authority":
11658
        // Run rewind and set state given parser, and "hostname".
11659
        parser.rewind();
11660
        parser.change_state(State::HOSTNAME, 0);
11661
        // Increment parser's token index by parser's token increment.
11662
        parser.token_index += parser.token_increment;
11663
        // Continue.
11664
        continue;
11665
      }
11666
11667
      // Run change state given parser, "done" and 0.
11668
      parser.change_state(State::DONE, 0);
11669
      // Break.
11670
      break;
11671
    }
11672
11673
    // If the result of running is a group open given parser is true:
11674
    if (parser.is_group_open()) {
11675
      // Increment parser's group depth by 1.
11676
      parser.group_depth += 1;
11677
      // Increment parser's token index by parser's token increment.
11678
      parser.token_index += parser.token_increment;
11679
      // Continue.
11680
      continue;
11681
    }
11682
11683
    // If parser's group depth is greater than 0:
11684
    if (parser.group_depth > 0) {
11685
      // If the result of running is a group close given parser is true, then
11686
      // decrement parser's group depth by 1.
11687
      if (parser.is_group_close()) {
11688
        parser.group_depth -= 1;
11689
      } else {
11690
        // Increment parser's token index by parser's token increment.
11691
        parser.token_index += parser.token_increment;
11692
        continue;
11693
      }
11694
    }
11695
11696
    // Switch on parser's state and run the associated steps:
11697
    switch (parser.state) {
11698
      case State::INIT: {
11699
        // If the result of running is a protocol suffix given parser is true:
11700
        if (parser.is_protocol_suffix()) {
11701
          // Run rewind and set state given parser and "protocol".
11702
          parser.rewind();
11703
          parser.change_state(State::PROTOCOL, 0);
11704
        }
11705
        break;
11706
      }
11707
      case State::PROTOCOL: {
11708
        // If the result of running is a protocol suffix given parser is true:
11709
        if (parser.is_protocol_suffix()) {
11710
          // Run compute protocol matches a special scheme flag given parser.
11711
          if (const auto error =
11712
                  parser.compute_protocol_matches_special_scheme_flag()) {
11713
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11714
            return tl::unexpected(*error);
11715
          }
11716
          // Let next state be "pathname".
11717
          auto next_state = State::PATHNAME;
11718
          // Let skip be 1.
11719
          auto skip = 1;
11720
          // If the result of running next is authority slashes given parser is
11721
          // true:
11722
          if (parser.next_is_authority_slashes()) {
11723
            // Set next state to "authority".
11724
            next_state = State::AUTHORITY;
11725
            // Set skip to 3.
11726
            skip = 3;
11727
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11728
            // Otherwise if parser's protocol matches a special scheme flag is
11729
            // true, then set next state to "authority".
11730
            next_state = State::AUTHORITY;
11731
          }
11732
11733
          // Run change state given parser, next state, and skip.
11734
          parser.change_state(next_state, skip);
11735
        }
11736
        break;
11737
      }
11738
      case State::AUTHORITY: {
11739
        // If the result of running is an identity terminator given parser is
11740
        // true, then run rewind and set state given parser and "username".
11741
        if (parser.is_an_identity_terminator()) {
11742
          parser.rewind();
11743
          parser.change_state(State::USERNAME, 0);
11744
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11745
                   parser.is_hash_prefix()) {
11746
          // Otherwise if any of the following are true:
11747
          // - the result of running is a pathname start given parser;
11748
          // - the result of running is a search prefix given parser; or
11749
          // - the result of running is a hash prefix given parser,
11750
          // then run rewind and set state given parser and "hostname".
11751
          parser.rewind();
11752
          parser.change_state(State::HOSTNAME, 0);
11753
        }
11754
        break;
11755
      }
11756
      case State::USERNAME: {
11757
        // If the result of running is a password prefix given parser is true,
11758
        // then run change state given parser, "password", and 1.
11759
        if (parser.is_password_prefix()) {
11760
          parser.change_state(State::PASSWORD, 1);
11761
        } else if (parser.is_an_identity_terminator()) {
11762
          // Otherwise if the result of running is an identity terminator given
11763
          // parser is true, then run change state given parser, "hostname",
11764
          // and 1.
11765
          parser.change_state(State::HOSTNAME, 1);
11766
        }
11767
        break;
11768
      }
11769
      case State::PASSWORD: {
11770
        // If the result of running is an identity terminator given parser is
11771
        // true, then run change state given parser, "hostname", and 1.
11772
        if (parser.is_an_identity_terminator()) {
11773
          parser.change_state(State::HOSTNAME, 1);
11774
        }
11775
        break;
11776
      }
11777
      case State::HOSTNAME: {
11778
        // If the result of running is an IPv6 open given parser is true, then
11779
        // increment parser's hostname IPv6 bracket depth by 1.
11780
        if (parser.is_an_ipv6_open()) {
11781
          parser.hostname_ipv6_bracket_depth += 1;
11782
        } else if (parser.is_an_ipv6_close()) {
11783
          // Otherwise if the result of running is an IPv6 close given parser is
11784
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11785
          parser.hostname_ipv6_bracket_depth -= 1;
11786
        } else if (parser.is_port_prefix() &&
11787
                   parser.hostname_ipv6_bracket_depth == 0) {
11788
          // Otherwise if the result of running is a port prefix given parser is
11789
          // true and parser's hostname IPv6 bracket depth is zero, then run
11790
          // change state given parser, "port", and 1.
11791
          parser.change_state(State::PORT, 1);
11792
        } else if (parser.is_pathname_start()) {
11793
          // Otherwise if the result of running is a pathname start given parser
11794
          // is true, then run change state given parser, "pathname", and 0.
11795
          parser.change_state(State::PATHNAME, 0);
11796
        } else if (parser.is_search_prefix()) {
11797
          // Otherwise if the result of running is a search prefix given parser
11798
          // is true, then run change state given parser, "search", and 1.
11799
          parser.change_state(State::SEARCH, 1);
11800
        } else if (parser.is_hash_prefix()) {
11801
          // Otherwise if the result of running is a hash prefix given parser is
11802
          // true, then run change state given parser, "hash", and 1.
11803
          parser.change_state(State::HASH, 1);
11804
        }
11805
11806
        break;
11807
      }
11808
      case State::PORT: {
11809
        // If the result of running is a pathname start given parser is true,
11810
        // then run change state given parser, "pathname", and 0.
11811
        if (parser.is_pathname_start()) {
11812
          parser.change_state(State::PATHNAME, 0);
11813
        } else if (parser.is_search_prefix()) {
11814
          // Otherwise if the result of running is a search prefix given parser
11815
          // is true, then run change state given parser, "search", and 1.
11816
          parser.change_state(State::SEARCH, 1);
11817
        } else if (parser.is_hash_prefix()) {
11818
          // Otherwise if the result of running is a hash prefix given parser is
11819
          // true, then run change state given parser, "hash", and 1.
11820
          parser.change_state(State::HASH, 1);
11821
        }
11822
        break;
11823
      }
11824
      case State::PATHNAME: {
11825
        // If the result of running is a search prefix given parser is true,
11826
        // then run change state given parser, "search", and 1.
11827
        if (parser.is_search_prefix()) {
11828
          parser.change_state(State::SEARCH, 1);
11829
        } else if (parser.is_hash_prefix()) {
11830
          // Otherwise if the result of running is a hash prefix given parser is
11831
          // true, then run change state given parser, "hash", and 1.
11832
          parser.change_state(State::HASH, 1);
11833
        }
11834
        break;
11835
      }
11836
      case State::SEARCH: {
11837
        // If the result of running is a hash prefix given parser is true, then
11838
        // run change state given parser, "hash", and 1.
11839
        if (parser.is_hash_prefix()) {
11840
          parser.change_state(State::HASH, 1);
11841
        }
11842
        break;
11843
      }
11844
      case State::HASH: {
11845
        // Do nothing
11846
        break;
11847
      }
11848
      default: {
11849
        // Assert: This step is never reached.
11850
        unreachable();
11851
      }
11852
    }
11853
11854
    // Increment parser's token index by parser's token increment.
11855
    parser.token_index += parser.token_increment;
11856
  }
11857
11858
  // If parser's result contains "hostname" and not "port", then set parser's
11859
  // result["port"] to the empty string.
11860
  if (parser.result.hostname && !parser.result.port) {
11861
    parser.result.port = "";
11862
  }
11863
11864
  // Return parser's result.
11865
  return parser.result;
11866
}
11867
11868
}  // namespace ada::url_pattern_helpers
11869
#endif  // ADA_INCLUDE_URL_PATTERN
11870
#endif
11871
/* end file include/ada/url_pattern_helpers-inl.h */
11872
/* begin file include/ada/url_pattern_list.h */
11873
/**
11874
 * @file url_pattern_list.h
11875
 * @brief Compiled set-level matching over URLPattern pathname patterns.
11876
 *
11877
 * `ada::url_pattern_list` answers the question "which of my N routes matches
11878
 * this pathname, and what are the parameter values?" in a single walk
11879
 * instead of a loop of N `url_pattern::exec` calls. The whole route set is
11880
 * compiled once, by ada::parse_url_pattern_list, into a segment trie with
11881
 * per-node dispatch (direct compares for small fanouts, a first-byte index
11882
 * at the root, perfect-hash projection for wide nodes). Matching a pathname
11883
 * against the compiled set is allocation-free and does not execute any
11884
 * regular expression for routes written in the common static / ":param" /
11885
 * "*" subset; other routes are matched through the regex provider, exactly
11886
 * as ada::url_pattern does.
11887
 *
11888
 * This implements the route-set ("URLPatternList") use case discussed in
11889
 * https://github.com/whatwg/urlpattern/issues/166 for the pathname component.
11890
 *
11891
 * Correctness does not depend on the fast path: inputs or routes that exceed
11892
 * the fast-path limits (documented on the constants in
11893
 * `ada::url_pattern_list_limits`) are matched by a sequential fallback with
11894
 * identical priority semantics, never rejected and never answered wrongly.
11895
 *
11896
 * @see https://urlpattern.spec.whatwg.org/
11897
 */
11898
#ifndef ADA_URL_PATTERN_LIST_H
11899
#define ADA_URL_PATTERN_LIST_H
11900
11901
11902
#include <array>
11903
#include <cstddef>
11904
#include <cstdint>
11905
#include <optional>
11906
#include <span>
11907
#include <string>
11908
#include <string_view>
11909
#include <vector>
11910
11911
#if ADA_INCLUDE_URL_PATTERN
11912
namespace ada {
11913
11914
/**
11915
 * The limits of the url_pattern_list fast path. None of them changes what a
11916
 * list matches: routes and inputs beyond a limit are matched by a sequential
11917
 * fallback with identical semantics.
11918
 * @namespace ada::url_pattern_list_limits
11919
 */
11920
namespace url_pattern_list_limits {
11921
11922
/**
11923
 * Maximum number of captures (":param" groups plus one trailing "*" group)
11924
 * a route may declare and still be compiled into the fast path. Routes with
11925
 * more groups are matched by the sequential fallback; their match is still
11926
 * correct, but only the first `max_captures_per_route` captures are reported
11927
 * (see url_pattern_list_match_result::captures_truncated).
11928
 */
11929
inline constexpr uint32_t max_captures_per_route = 8;
11930
11931
/**
11932
 * Maximum number of '/'-separated segments an input pathname may have and
11933
 * still be matched by the fast path. Longer inputs fall back to the
11934
 * sequential matcher and are still matched correctly.
11935
 */
11936
inline constexpr uint32_t max_fast_path_segments = 24;
11937
11938
/**
11939
 * Maximum input pathname length (bytes) accepted by the fast path; segment
11940
 * offsets are tracked in 16-bit integers internally. Longer inputs fall back
11941
 * to the sequential matcher and are still matched correctly.
11942
 */
11943
inline constexpr uint32_t max_fast_path_pathname_length = 4096;
11944
11945
/**
11946
 * Maximum number of pattern segments a route may have and still be compiled
11947
 * into the trie fast path. Deeper routes are matched by the sequential
11948
 * fallback with identical semantics.
11949
 */
11950
inline constexpr uint32_t max_trie_pattern_segments = 16;
11951
11952
}  // namespace url_pattern_list_limits
11953
11954
/**
11955
 * The result of url_pattern_list::match. A winning route reports its group
11956
 * values in one of two forms, mirroring url_pattern_list::group_names():
11957
 *
11958
 * - Routes in the static / ":param" / "*" subset report zero-allocation
11959
 *   captures: (offset, length) slices of the pathname passed to match, in
11960
 *   capture order (":param" groups left to right, then the "*" group).
11961
 *   `regexp_route` is false and `regexp_groups` is empty.
11962
 * - Routes matched through the regex provider (patterns with custom regexp
11963
 *   groups or "?" / "+" / "*" modifiers) report the group values the
11964
 *   provider's regex_search returned, exactly as url_pattern::exec would for
11965
 *   the pathname component: `regexp_route` is true, `regexp_groups[i]` is the
11966
 *   value of group_names(route_index)[i] (nullopt for a group that did not
11967
 *   participate), and capture_count is 0.
11968
 *
11969
 * Routes with more than url_pattern_list_limits::max_captures_per_route
11970
 * groups report only the first max_captures_per_route slices and set
11971
 * captures_truncated.
11972
 */
11973
struct url_pattern_list_match_result {
11974
  /** One capture: an (offset, length) slice of the matched pathname. */
11975
  struct capture {
11976
    uint32_t offset = 0;
11977
    uint32_t length = 0;
11978
  };
11979
  /** Index of the winning route in the creation order, or -1 for no match. */
11980
  int32_t route_index = -1;
11981
  /** Number of valid entries in captures. */
11982
  uint32_t capture_count = 0;
11983
  /** True when the route has more groups than could be reported as slices. */
11984
  bool captures_truncated = false;
11985
  /** True when the winning route was matched through the regex provider; its
11986
   * group values are in regexp_groups. */
11987
  bool regexp_route = false;
11988
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
11989
      captures{};
11990
  /** Group values of a regexp route, as returned by the provider. */
11991
  std::vector<std::optional<std::string>> regexp_groups{};
11992
11993
0
  [[nodiscard]] bool has_match() const noexcept { return route_index >= 0; }
11994
};
11995
11996
/**
11997
 * @private
11998
 * The compiled tables url_pattern_list::match walks, and the inline matcher
11999
 * over them (url_pattern_list-inl.h). Nothing here is part of the supported
12000
 * API. The route-set compiler that produces these tables is not part of the
12001
 * public headers at all (src/url_pattern_list_compiler.h).
12002
 * @namespace ada::url_pattern_list_detail
12003
 */
12004
namespace url_pattern_list_detail {
12005
12006
/** @private One trie node. Nodes with a projection dispatch keep their hash
12007
 * payload in a separate hash_record, so the common 0/1/direct nodes stay at
12008
 * 24 bytes. */
12009
struct node_record {
12010
  // First static edge (edges are laid out per node, contiguously).
12011
  int32_t first_child = 0;
12012
  // Child node index, -2 - route (pure-leaf shortcut), or -1.
12013
  int32_t param_child = -1;
12014
  // Route index of a "*" ending at this prefix, or -1.
12015
  int32_t wild_route = -1;
12016
  // Route ending exactly here, or -1.
12017
  int32_t terminal_route = -1;
12018
  uint16_t n_static = 0;
12019
  // 0 none, 1 direct compares, 2 projection, 3 linear scan, 4 first-byte
12020
  // index (root only).
12021
  uint8_t dispatch = 0;
12022
  // 1 if the node still has a param/wildcard alternative to backtrack to.
12023
  uint8_t has_alternative = 0;
12024
  // Index of the node's hash_record (dispatch == 2 only).
12025
  uint32_t hash_index = 0;
12026
};
12027
12028
/** @private Projection-dispatch payload of a hashed node. */
12029
struct hash_record {
12030
  uint64_t multiplier = 0;
12031
  uint32_t slot_base = 0;
12032
  // Witness plan: 3 nibble ids | count << 12 | use_len << 14.
12033
  uint16_t witness_plan = 0;
12034
  // Slot table size == 1 << slot_bits.
12035
  uint8_t slot_bits = 0;
12036
  uint8_t reserved = 0;
12037
};
12038
12039
/** @private One static edge; keys <= 16 bytes verify from the packed
12040
 * windows alone. */
12041
struct edge_record {
12042
  // Key bytes [0, min(8, len)), little-endian, zero-padded.
12043
  uint64_t prefix = 0;
12044
  // Key bytes [max(0, len - 8), len), little-endian, zero-padded.
12045
  uint64_t suffix = 0;
12046
  // Child node index; negative values encode -2 - route pure-leaf shortcuts.
12047
  int32_t node = -1;
12048
  // Full key in the blob (read only when key_length > 16).
12049
  uint32_t key_offset = 0;
12050
  uint16_t key_length = 0;
12051
};
12052
12053
/** @private How one route of the set is matched. */
12054
enum class route_mode : uint8_t {
12055
  // Compiled into the trie fast path.
12056
  trie = 0,
12057
  // Safe static/":param"/"*" syntax beyond a fast-path build limit: matched
12058
  // by the sequential segment matcher.
12059
  sequential = 1,
12060
  // Needs URLPattern regexp semantics: matched through the regex provider.
12061
  regexp = 2,
12062
};
12063
12064
/** @private Everything the matcher knows about one route after a hit. */
12065
struct route_record {
12066
  // Packed per-segment kinds, two bits per segment, most significant first;
12067
  // (kind_sequence, kind_length, route index) compared as a tuple is the
12068
  // specificity order.
12069
  uint64_t kind_sequence = 0;
12070
  // Sequential-mode routes: their segments in the segment table.
12071
  uint32_t segment_first = 0;
12072
  uint32_t segment_count = 0;
12073
  // Auxiliary routes that can outrank this route when it wins the fast
12074
  // path: a range of the aux table (usually empty).
12075
  uint32_t challenger_first = 0;
12076
  uint32_t challenger_count = 0;
12077
  // Ordinal among the regexp-mode routes, or -1.
12078
  int32_t regexp_component = -1;
12079
  uint8_t kind_length = 0;
12080
  route_mode mode = route_mode::sequential;
12081
  // Trie-mode routes: capture positions.
12082
  uint8_t n_params = 0;
12083
  uint8_t wild = 0;
12084
  std::array<uint8_t, url_pattern_list_limits::max_captures_per_route>
12085
      param_positions{};
12086
};
12087
12088
/** @private One pattern segment of a sequential-mode route. */
12089
struct segment_record {
12090
  uint32_t text_offset = 0;
12091
  uint32_t text_length = 0;
12092
  // 0 literal, 1 ":param", 2 "*".
12093
  uint8_t kind = 0;
12094
};
12095
12096
/**
12097
 * @private
12098
 * The compiled route set: one arena holding every table, addressed by
12099
 * section offsets, plus the per-route group names. Sections are 8-byte
12100
 * aligned; the blob is zero-padded by 8 bytes so short key windows can be
12101
 * loaded whole.
12102
 */
12103
struct compiled_routes {
12104
  std::vector<uint8_t> arena{};
12105
  std::vector<std::vector<std::string>> group_names{};
12106
  uint32_t nodes_offset = 0;
12107
  uint32_t hashes_offset = 0;
12108
  uint32_t edges_offset = 0;
12109
  uint32_t slots_offset = 0;
12110
  uint32_t blob_offset = 0;
12111
  uint32_t routes_offset = 0;
12112
  uint32_t segments_offset = 0;
12113
  uint32_t aux_offset = 0;
12114
  uint32_t root_index_offset = 0;
12115
  uint32_t n_routes = 0;
12116
  // All auxiliary routes (regexp mode and sequential mode) in insertion
12117
  // order: the aux table's first n_aux_all entries; challenger ranges
12118
  // follow.
12119
  uint32_t n_aux_all = 0;
12120
  uint8_t ignore_case = 0;
12121
12122
  template <typename T>
12123
0
  [[nodiscard]] const T* section(uint32_t offset) const noexcept {
12124
0
    return reinterpret_cast<const T*>(arena.data() + offset);
12125
0
  }
Unexecuted instantiation: char const* ada::url_pattern_list_detail::compiled_routes::section<char>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::route_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::route_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::segment_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::segment_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::node_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::node_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::hash_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::hash_record>(unsigned int) const
Unexecuted instantiation: ada::url_pattern_list_detail::edge_record const* ada::url_pattern_list_detail::compiled_routes::section<ada::url_pattern_list_detail::edge_record>(unsigned int) const
Unexecuted instantiation: unsigned char const* ada::url_pattern_list_detail::compiled_routes::section<unsigned char>(unsigned int) const
Unexecuted instantiation: unsigned short const* ada::url_pattern_list_detail::compiled_routes::section<unsigned short>(unsigned int) const
12126
};
12127
12128
/** @private Result of the fast-path engine or of the sequential matcher. */
12129
struct engine_result {
12130
  using capture = url_pattern_list_match_result::capture;
12131
  int32_t route = -1;
12132
  uint32_t capture_count = 0;
12133
  // False when the input exceeded a fast-path limit and the caller must run
12134
  // the sequential fallback over the whole route set.
12135
  bool within_fast_path = true;
12136
  bool captures_truncated = false;
12137
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
12138
      captures{};
12139
};
12140
12141
/**
12142
 * @private
12143
 * Parses and compiles a set of pathname patterns (already base-URL
12144
 * processed). Routes that need the regex provider come back with
12145
 * route_record::regexp_component set to their ordinal in insertion order;
12146
 * the caller compiles those components and fills their group names.
12147
 */
12148
tl::expected<compiled_routes, errors> compile_pathname_patterns(
12149
    std::span<const std::string> patterns, bool ignore_case);
12150
12151
/**
12152
 * @private
12153
 * Sequential reference matcher for one non-regexp route of the compiled set;
12154
 * matches any input (no fast-path limits) with the same semantics as the
12155
 * compiled engine. With `fold_input`, input bytes are ASCII case-folded on
12156
 * the fly (the compiled literals are already folded). On a match, fills
12157
 * `result` (route index is NOT set) and returns true.
12158
 */
12159
bool match_route_sequential(const compiled_routes& tables, uint32_t route,
12160
                            std::string_view pathname, bool fold_input,
12161
                            engine_result& result) noexcept;
12162
12163
/**
12164
 * @private
12165
 * True when the bytes [p, p + n) hold no line terminator. A "*" segment
12166
 * compiles to "(.*)" in the URLPattern regexp, and "." in an ECMAScript
12167
 * regular expression does not match LF or CR, so a wildcard may not capture
12168
 * past one. Deliberately not inlined into the walk (it costs the static and
12169
 * ":param" paths, which never run it), and deliberately defined in
12170
 * url_pattern_list.cpp rather than here: MSVC's ada_never_inline carries no
12171
 * inline linkage, so a definition in this header is emitted in every
12172
 * translation unit.
12173
 */
12174
bool wildcard_tail_ok(const char* p, uint32_t n) noexcept;
12175
12176
/**
12177
 * @private
12178
 * Cheap pre-check for a regexp-mode route before running the provider: the
12179
 * part of the route's segment shape that is certain (see the compiler's
12180
 * approximate_kind_sequence) must fit the pathname -- its anchored literal
12181
 * segments, and its exact segment count when every group is a ":name"
12182
 * segment wildcard. False proves the regular expression cannot match.
12183
 */
12184
bool match_regexp_shape(const compiled_routes& tables, uint32_t route,
12185
                        std::string_view pathname, bool fold_input) noexcept;
12186
12187
/**
12188
 * @private
12189
 * True when route `a` (at insertion index `a_index`) outranks route `b` (at
12190
 * `b_index`): lexicographically smaller kind sequence, insertion order as the
12191
 * tiebreak.
12192
 */
12193
constexpr bool outranks(uint64_t a_sequence, uint32_t a_length, size_t a_index,
12194
                        uint64_t b_sequence, uint32_t b_length,
12195
0
                        size_t b_index) noexcept {
12196
0
  if (a_sequence != b_sequence) {
12197
0
    return a_sequence < b_sequence;
12198
0
  }
12199
0
  if (a_length != b_length) {
12200
0
    return a_length < b_length;
12201
0
  }
12202
0
  return a_index < b_index;
12203
0
}
12204
12205
}  // namespace url_pattern_list_detail
12206
12207
template <url_pattern_regex::regex_concept regex_provider>
12208
class url_pattern_list;
12209
12210
/**
12211
 * @private
12212
 * url_pattern_list befriends these two specializations, so they have to be
12213
 * declared before it: a friend declaration that redeclares the function
12214
 * template instead is not matched by every compiler we support (Apple clang
12215
 * 15 rejects the resulting member access). The documented declarations are
12216
 * in implementation.h, next to parse_url_pattern, and carry the default
12217
 * arguments: this header includes url_pattern.h, which includes
12218
 * implementation.h, so those declarations are always seen first, and a
12219
 * function template may not gain default arguments in a later declaration.
12220
 */
12221
template <url_pattern_regex::regex_concept regex_provider>
12222
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12223
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
12224
                       const std::string_view* base_url,
12225
                       const url_pattern_options* options);
12226
12227
/** @private See above. */
12228
template <url_pattern_regex::regex_concept regex_provider>
12229
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12230
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
12231
12232
/**
12233
 * @brief A compiled set of URLPattern pathname patterns with one-shot
12234
 * matching.
12235
 *
12236
 * Build once with ada::parse_url_pattern_list, then call match() per
12237
 * request. Matching within the fast-path limits is allocation-free and
12238
 * regex-free for routes written in the static / ":param" / "*" subset; other
12239
 * routes are matched through the regex provider (the same regex_search call
12240
 * url_pattern::exec makes) and still participate in the priority order.
12241
 *
12242
 * Match priority is specificity order, not registration order: routes are
12243
 * compared by their per-segment kind sequence (literal < ":param" < "*",
12244
 * lexicographically from the first segment), with insertion order breaking
12245
 * ties. This is the priority scheme of find-my-way (Fastify) and Express-
12246
 * style routers. Whether a standardized URLPatternList should instead use
12247
 * pure first-match-in-insertion-order semantics is an open question; the
12248
 * compiled representation supports either, and the priority rule is
12249
 * deliberately centralized in url_pattern_list_detail::outranks.
12250
 *
12251
 * Scope (v1): the pathname component only. Other URL components are treated
12252
 * as fully wildcarded; match() takes an already-extracted pathname (for
12253
 * example ada::url_aggregator::get_pathname()). Inputs are matched as given
12254
 * and are expected to be in canonical (percent-encoded) form; the pattern
12255
 * side is canonicalized by the URLPattern pattern parser at creation. The
12256
 * subset follows the URLPattern regexp it stands for: a ":param" segment
12257
 * ("[^/]+?") is one non-empty segment, and a "*" tail ("(.*)") matches any
12258
 * bytes except the line terminators LF and CR, which "." never matches.
12259
 *
12260
 * With url_pattern_options::ignore_case, regexp routes are compiled through
12261
 * the provider with the flag set (as url_pattern does), and the static /
12262
 * ":param" / "*" subset compares literal segments with ASCII case folding,
12263
 * which is what a case-insensitive regular expression does over the ASCII
12264
 * text of a canonical pathname.
12265
 *
12266
 * @tparam regex_provider The regex implementation used only for routes that
12267
 *         need URLPattern regexp semantics. Must satisfy
12268
 *         url_pattern_regex::regex_concept.
12269
 */
12270
template <url_pattern_regex::regex_concept regex_provider>
12271
class url_pattern_list {
12272
 public:
12273
  url_pattern_list() = default;
12274
12275
  /**
12276
   * Matches a pathname against the set and returns the winning route with
12277
   * its captures (see url_pattern_list_match_result). The fast path is
12278
   * allocation-free; inputs beyond the fast-path limits are matched by the
12279
   * sequential fallback with identical semantics.
12280
   */
12281
  [[nodiscard]] url_pattern_list_match_result match(
12282
      std::string_view pathname) const;
12283
12284
  /** Number of routes in the set. */
12285
  [[nodiscard]] size_t size() const noexcept { return patterns_.size(); }
12286
12287
  /** The pathname pattern string the route at `route_index` was created
12288
   * from (after base URL processing, if a base URL was given). */
12289
  [[nodiscard]] std::string_view pattern(size_t route_index) const
12290
      ada_lifetime_bound {
12291
    return patterns_[route_index];
12292
  }
12293
12294
  /**
12295
   * Capture group names of the route at `route_index`, in the order of
12296
   * url_pattern_list_match_result::captures / regexp_groups ("*" groups have
12297
   * numeric names, per URLPattern).
12298
   */
12299
  [[nodiscard]] const std::vector<std::string>& group_names(
12300
      size_t route_index) const ada_lifetime_bound {
12301
    return compiled_.group_names[route_index];
12302
  }
12303
12304
  /** The ignore_case option the set was created with. */
12305
  [[nodiscard]] bool ignore_case() const noexcept {
12306
    return compiled_.ignore_case != 0;
12307
  }
12308
12309
  friend tl::expected<url_pattern_list, errors>
12310
  parse_url_pattern_list<regex_provider>(
12311
      std::span<const std::string_view> pathname_patterns,
12312
      const std::string_view* base_url, const url_pattern_options* options);
12313
12314
  friend tl::expected<url_pattern_list, errors> parse_url_pattern_list<
12315
      regex_provider>(std::span<const url_pattern<regex_provider>> patterns);
12316
12317
 private:
12318
  /** @private Compiles the (already processed) pattern strings; the regexp
12319
   * components are then supplied by the caller in insertion order. */
12320
  static tl::expected<url_pattern_list, errors> create(
12321
      std::vector<std::string>&& pathname_patterns, bool ignore_case);
12322
12323
  /** @private Tests one auxiliary route (sequential or regexp mode) and,
12324
   * when it matches and outranks `best`, replaces `best` with it. `probe`
12325
   * is the (possibly case-folded) pathname for the sequential matcher,
12326
   * `pathname` the original for the provider. */
12327
  void consider_route(
12328
      uint32_t route_index, std::string_view pathname, std::string_view probe,
12329
      bool fold_input, url_pattern_list_detail::engine_result& best,
12330
      std::vector<std::optional<std::string>>& best_groups) const;
12331
12332
  /** @private The pattern strings, by route index. */
12333
  std::vector<std::string> patterns_{};
12334
  /** @private The compiled tables. */
12335
  url_pattern_list_detail::compiled_routes compiled_{};
12336
  /** @private Compiled pathname components of the regexp-mode routes, in
12337
   * insertion order. */
12338
  std::vector<url_pattern_component<regex_provider>> regexp_components_{};
12339
};
12340
12341
}  // namespace ada
12342
#endif  // ADA_INCLUDE_URL_PATTERN
12343
#endif  // ADA_URL_PATTERN_LIST_H
12344
/* end file include/ada/url_pattern_list.h */
12345
/* begin file include/ada/url_pattern_list-inl.h */
12346
/**
12347
 * @file url_pattern_list-inl.h
12348
 * @brief The url_pattern_list matcher (inline) and the class template's
12349
 * member definitions.
12350
 *
12351
 * The walk over the compiled tables lives here, as ada_really_inline
12352
 * functions, so that it inlines into url_pattern_list::match; the route-set
12353
 * compiler that builds the tables is in its own translation unit
12354
 * (src/url_pattern_list.cpp) and is not part of the public headers.
12355
 */
12356
#ifndef ADA_URL_PATTERN_LIST_INL_H
12357
#define ADA_URL_PATTERN_LIST_INL_H
12358
12359
12360
#include <bit>
12361
#include <cstdint>
12362
#include <cstring>
12363
#include <optional>
12364
#include <string>
12365
#include <string_view>
12366
#include <utility>
12367
#include <vector>
12368
12369
#if ADA_NEON
12370
#include <arm_neon.h>
12371
#endif
12372
12373
#if ADA_INCLUDE_URL_PATTERN
12374
namespace ada::url_pattern_list_detail {
12375
12376
// ---- byte helpers ----------------------------------------------------------
12377
12378
// Unaligned 8-byte little-endian load; all 8 bytes must be readable. The
12379
// byte-assembly fallback keeps big-endian targets correct (the compiler
12380
// lowers it to a single load plus byte swap).
12381
0
ada_really_inline uint64_t load8_le(const char* p) noexcept {
12382
0
  if constexpr (std::endian::native == std::endian::little) {
12383
0
    uint64_t x;
12384
0
    std::memcpy(&x, p, 8);
12385
0
    return x;
12386
  } else {
12387
    uint64_t x = 0;
12388
    for (uint32_t j = 0; j < 8; j++) {
12389
      x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12390
    }
12391
    return x;
12392
  }
12393
0
}
12394
12395
// Bytes [0, n) of p packed little-endian, zero-padded; reads exactly n
12396
// bytes (n < 8). This is the byte-compare path for short tails.
12397
0
ada_really_inline uint64_t gather_le(const char* p, uint32_t n) noexcept {
12398
0
  uint64_t x = 0;
12399
0
  for (uint32_t j = 0; j < n; j++) {
12400
0
    x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12401
0
  }
12402
0
  return x;
12403
0
}
12404
12405
// Mask selecting the low n bytes, 1 <= n <= 7.
12406
0
constexpr uint64_t low_bytes_mask(uint32_t n) noexcept {
12407
0
  return ~0ull >> (64 - 8 * n);
12408
0
}
12409
12410
// ASCII case folding of n bytes from src into dst (the compiler vectorizes
12411
// this loop). Only ASCII letters change: canonical pathnames are ASCII, and
12412
// this is what a case-insensitive regular expression does over them.
12413
0
inline void ascii_fold(const char* src, uint32_t n, char* dst) noexcept {
12414
0
  for (uint32_t i = 0; i < n; i++) {
12415
0
    const uint8_t c = static_cast<uint8_t>(src[i]);
12416
0
    dst[i] = static_cast<char>(c | ((c >= 'A' && c <= 'Z') ? 0x20u : 0u));
12417
0
  }
12418
0
}
12419
12420
// ---- witness machinery -----------------------------------------------------
12421
// Projection dispatch for wide trie nodes. A witness id names a feature of a
12422
// byte string:
12423
//   0..7   the byte at absolute offset id           (0 when out of range)
12424
//   8..15  the byte at offset id-8 counted from the end
12425
// A projection is three witness bytes plus the length packed into one
12426
// uint64; a perfect multiplier turns it into a slot index. Builder and
12427
// matcher call the same accessors and the same packing, so a slot table
12428
// cannot disagree with the gather that reads it.
12429
12430
0
constexpr uint8_t length_byte(uint32_t len) noexcept {
12431
0
  return len < 255u ? static_cast<uint8_t>(len) : static_cast<uint8_t>(255);
12432
0
}
12433
12434
constexpr uint8_t witness_byte(const char* p, uint32_t len,
12435
0
                               uint8_t id) noexcept {  // ids 0..15
12436
0
  uint32_t o = id & 7u;
12437
0
  bool in = o < len;
12438
0
  uint32_t pos = (id & 8u) ? len - 1u - o : o;  // wraps when !in; masked then
12439
0
  pos = in ? pos : 0u;
12440
0
  uint8_t v = len ? static_cast<uint8_t>(p[pos]) : static_cast<uint8_t>(0);
12441
0
  return in ? v : static_cast<uint8_t>(0);
12442
0
}
12443
12444
constexpr uint64_t pack_projection(uint8_t f0, uint8_t f1, uint8_t f2,
12445
0
                                   uint8_t f3) noexcept {
12446
0
  return (static_cast<uint64_t>(f0) << 24) | (static_cast<uint64_t>(f1) << 16) |
12447
0
         (static_cast<uint64_t>(f2) << 8) | static_cast<uint64_t>(f3);
12448
0
}
12449
12450
// Perfect-multiplier slot index; b <= 12 everywhere, so the shift is in
12451
// range and the product needs no modulus.
12452
constexpr uint64_t slot_of(uint64_t proj, uint64_t multiplier,
12453
0
                           uint8_t b) noexcept {
12454
0
  return (proj * multiplier) >> (64 - b);
12455
0
}
12456
12457
// Fixed-shape projection of one string: always 3 witness bytes plus the
12458
// length. Duplicated ids add no information but keep the gather uniform.
12459
constexpr uint64_t project(const char* p, uint32_t len,
12460
0
                           uint16_t plan) noexcept {
12461
0
  return pack_projection(
12462
0
      witness_byte(p, len, static_cast<uint8_t>(plan & 15u)),
12463
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 4) & 15u)),
12464
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 8) & 15u)),
12465
0
      length_byte(len));
12466
0
}
12467
12468
// ---- segment scan ----------------------------------------------------------
12469
12470
// Splits the pathname into segments in one pass, in place: only segment
12471
// starts are recorded, with a sentinel soff[nseg] = ulen + 1 so that
12472
// seg_len(i) == soff[i + 1] - soff[i] - 1. Returns the segment count, or 0
12473
// when the input has more than max_fast_path_segments segments (the caller
12474
// falls back to the sequential matcher). Requires ulen >= 1 and
12475
// url[0] == '/'.
12476
//
12477
// Two implementations with the same contract: the portable SWAR scan (8
12478
// bytes per step, an exact zero-byte test on x ^ '/'-fill with no false
12479
// positives, bytes >= 0x80 included, and a byte-exact partial load for the
12480
// tail, so the input is never over-read), and on AArch64 a NEON scan for
12481
// inputs of 16 bytes or more (16 bytes per step, the '/' compare narrowed
12482
// to one nibble per byte, the last block overlapping the previous one so
12483
// the input is never over-read). The 1k+ pathname benchmark in the PR
12484
// discussion is what keeps the NEON scan: it is faster at every length and
12485
// 3-4x faster past 1 KB.
12486
struct segment_emitter {
12487
  uint16_t* soff;
12488
  uint32_t nseg = 0;
12489
  uint32_t start = 1;
12490
  // One segment start per set bit of `lanes`; a lane is 1 << shift bits
12491
  // wide (8 for the SWAR masks, 4 for the NEON nibble masks).
12492
  ada_really_inline bool emit(uint64_t lanes, uint32_t base,
12493
0
                              unsigned shift) noexcept {
12494
0
    using url_pattern_list_limits::max_fast_path_segments;
12495
0
    while (lanes) {
12496
0
      const uint32_t pos =
12497
0
          base + (static_cast<uint32_t>(std::countr_zero(lanes)) >> shift);
12498
0
      if (nseg >= max_fast_path_segments) {
12499
0
        return false;
12500
0
      }
12501
0
      soff[nseg++] = static_cast<uint16_t>(start);
12502
0
      start = pos + 1;
12503
0
      lanes &= lanes - 1;
12504
0
    }
12505
0
    return true;
12506
0
  }
12507
0
  ada_really_inline uint32_t finish(uint32_t ulen) noexcept {
12508
0
    using url_pattern_list_limits::max_fast_path_segments;
12509
0
    if (nseg >= max_fast_path_segments) {
12510
0
      return 0;
12511
0
    }
12512
0
    soff[nseg++] = static_cast<uint16_t>(start);
12513
0
    soff[nseg] = static_cast<uint16_t>(ulen + 1);  // sentinel
12514
0
    return nseg;
12515
0
  }
12516
};
12517
12518
ada_really_inline uint32_t scan_segments_swar(const char* url, uint32_t ulen,
12519
0
                                              uint16_t* soff) noexcept {
12520
0
  constexpr uint64_t slashes = 0x2F2F2F2F2F2F2F2Full;
12521
0
  constexpr uint64_t low7 = 0x7F7F7F7F7F7F7F7Full;
12522
0
  // Bit 7 of every byte of x that is zero, exactly: (b & 0x7F) + 0x7F has
12523
0
  // bit 7 set iff the low bits are non-zero, b itself has it set iff b is
12524
0
  // >= 0x80, and no lane can carry into its neighbour.
12525
0
  const auto zero_lanes = [](uint64_t x) noexcept {
12526
0
    return ~(((x & low7) + low7) | x | low7);
12527
0
  };
12528
0
  segment_emitter out{soff};
12529
0
  uint32_t i = 1;
12530
0
  for (; i + 8 <= ulen; i += 8) {
12531
0
    const uint64_t lanes = zero_lanes(load8_le(url + i) ^ slashes);
12532
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12533
0
      return 0;
12534
0
    }
12535
0
  }
12536
0
  if (i < ulen) {  // tail of 1..7 bytes: zero padding is never a '/'
12537
0
    const uint64_t lanes = zero_lanes(gather_le(url + i, ulen - i) ^ slashes);
12538
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12539
0
      return 0;
12540
0
    }
12541
0
  }
12542
0
  return out.finish(ulen);
12543
0
}
12544
12545
#if ADA_NEON
12546
ada_really_inline uint32_t scan_segments_neon(const char* url, uint32_t ulen,
12547
                                              uint16_t* soff) noexcept {
12548
  // Requires ulen >= 16.
12549
  const uint8x16_t slash = vdupq_n_u8('/');
12550
  // One bit per byte lane that holds a '/': compare, narrow each 16-bit
12551
  // pair to its high nibble, keep bit 0 of every nibble.
12552
  const auto slash_lanes = [&](const char* p) noexcept {
12553
    const uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
12554
    const uint64_t nibbles =
12555
        vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(
12556
                          vreinterpretq_u16_u8(vceqq_u8(v, slash)), 4)),
12557
                      0);
12558
    return nibbles & 0x1111111111111111ull;
12559
  };
12560
  segment_emitter out{soff};
12561
  uint32_t i = 0;
12562
  for (; i + 16 <= ulen; i += 16) {
12563
    uint64_t lanes = slash_lanes(url + i);
12564
    if (i == 0) {
12565
      lanes &= ~0xFull;  // the leading '/' is not a separator
12566
    }
12567
    if (lanes != 0 && !out.emit(lanes, i, 2)) {
12568
      return 0;
12569
    }
12570
  }
12571
  if (i < ulen) {  // overlapped last block: drop the lanes already scanned
12572
    const uint32_t off = ulen - 16;
12573
    const uint64_t lanes = slash_lanes(url + off) & (~0ull << ((i - off) * 4));
12574
    if (lanes != 0 && !out.emit(lanes, off, 2)) {
12575
      return 0;
12576
    }
12577
  }
12578
  return out.finish(ulen);
12579
}
12580
#endif  // ADA_NEON
12581
12582
ada_really_inline uint32_t scan_segments(const char* url, uint32_t ulen,
12583
0
                                         uint16_t* soff) noexcept {
12584
0
#if ADA_NEON
12585
0
  if (ulen >= 16) {
12586
0
    return scan_segments_neon(url, ulen, soff);
12587
0
  }
12588
0
#endif
12589
0
  return scan_segments_swar(url, ulen, soff);
12590
0
}
12591
12592
// ---- verify / dispatch -----------------------------------------------------
12593
12594
// One input segment prepared for dispatch: its bytes, its length, and how
12595
// many bytes are readable from p (to the end of the pathname).
12596
struct segment_ref {
12597
  const char* p;
12598
  uint32_t len;
12599
  uint32_t avail;
12600
};
12601
12602
// Segment-vs-edge compare. Keys of 8..16 bytes verify from two whole-word
12603
// loads inside the segment; shorter keys from one masked load when the
12604
// input has 8 readable bytes, else from a byte gather (short tails such as
12605
// "42" or "me"); longer keys through memcmp against the blob. An empty key
12606
// (a pattern such as "/users/") matches exactly the empty segment.
12607
ada_really_inline bool verify_edge(const edge_record& e, const segment_ref& s,
12608
0
                                   const char* blob) noexcept {
12609
0
  const uint32_t len = s.len;
12610
0
  if (len != e.key_length) {
12611
0
    return false;
12612
0
  }
12613
0
  if (len == 0) {
12614
0
    return true;
12615
0
  }
12616
0
  if (len > 16) {
12617
0
    return std::memcmp(s.p, blob + e.key_offset, len) == 0;
12618
0
  }
12619
0
  if (len >= 8) {
12620
0
    return ((load8_le(s.p) ^ e.prefix) |
12621
0
            (load8_le(s.p + len - 8) ^ e.suffix)) == 0;
12622
0
  }
12623
0
  const uint64_t x = s.avail >= 8 ? (load8_le(s.p) & low_bytes_mask(len))
12624
0
                                  : gather_le(s.p, len);
12625
0
  return x == e.prefix;
12626
0
}
12627
12628
// The typed view of the arena the walk reads; derived once per match.
12629
struct table_view {
12630
  const node_record* nodes;
12631
  const hash_record* hashes;
12632
  const edge_record* edges;
12633
  const uint8_t* slots;
12634
  const char* blob;
12635
  const uint16_t* root_index;
12636
};
12637
12638
0
ada_really_inline table_view view_of(const compiled_routes& r) noexcept {
12639
0
  return table_view{r.section<node_record>(r.nodes_offset),
12640
0
                    r.section<hash_record>(r.hashes_offset),
12641
0
                    r.section<edge_record>(r.edges_offset),
12642
0
                    r.section<uint8_t>(r.slots_offset),
12643
0
                    r.section<char>(r.blob_offset),
12644
0
                    r.section<uint16_t>(r.root_index_offset)};
12645
0
}
12646
12647
// Static-child dispatch: returns the child ordinal within the node or -1.
12648
// Correctness never depends on the projection or the index: the candidate
12649
// is always confirmed by a full segment compare, so a bad table can only
12650
// cost time, never change an answer.
12651
ada_really_inline int32_t dispatch_static(const table_view& t,
12652
                                          const node_record& nd,
12653
0
                                          const segment_ref& s) noexcept {
12654
0
  const edge_record* e = t.edges + nd.first_child;
12655
0
  switch (nd.dispatch) {
12656
0
    case 0:
12657
0
      return -1;
12658
0
    case 1: {  // direct: up to 8 compares, each gated by the key length
12659
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12660
0
        if (verify_edge(e[j], s, t.blob)) {
12661
0
          return static_cast<int32_t>(j);
12662
0
        }
12663
0
      }
12664
0
      return -1;
12665
0
    }
12666
0
    case 2: {  // projection: gather -> multiply -> slot -> verify
12667
0
      const hash_record& h = t.hashes[nd.hash_index];
12668
0
      const uint64_t proj = project(s.p, s.len, h.witness_plan);
12669
0
      const uint8_t ord =
12670
0
          t.slots[h.slot_base + slot_of(proj, h.multiplier, h.slot_bits)];
12671
0
      if (ord == 0xFF) {
12672
0
        return -1;
12673
0
      }
12674
0
      return verify_edge(e[ord], s, t.blob) ? static_cast<int32_t>(ord) : -1;
12675
0
    }
12676
0
    case 4: {  // root first-byte index: jump to the run of children that
12677
0
               // share the segment's first byte (children are sorted by it)
12678
0
      if (s.len == 0) {
12679
0
        return -1;  // no first byte; an indexed root has no empty key
12680
0
      }
12681
0
      const uint8_t b = static_cast<uint8_t>(s.p[0]);
12682
0
      uint32_t j = t.root_index[b];
12683
0
      if (j == 0xFFFF) {
12684
0
        return -1;
12685
0
      }
12686
0
      for (; j < nd.n_static && static_cast<uint8_t>(e[j].prefix & 0xFFu) == b;
12687
0
           j++) {
12688
0
        if (verify_edge(e[j], s, t.blob)) {
12689
0
          return static_cast<int32_t>(j);
12690
0
        }
12691
0
      }
12692
0
      return -1;
12693
0
    }
12694
0
    default: {  // linear demotion rung (also carries fanout > 254)
12695
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12696
0
        if (verify_edge(e[j], s, t.blob)) {
12697
0
          return static_cast<int32_t>(j);
12698
0
        }
12699
0
      }
12700
0
      return -1;
12701
0
    }
12702
0
  }
12703
0
}
12704
12705
// ---- the match residual ----------------------------------------------------
12706
12707
// Matches `pathname` against the compiled tables. When the input is out of
12708
// the fast-path contract, returns with within_fast_path == false and no
12709
// answer; the caller falls back to the sequential matcher.
12710
ada_really_inline void match_compiled(const compiled_routes& r, const char* url,
12711
                                      uint32_t ulen,
12712
0
                                      engine_result& out) noexcept {
12713
0
  using url_pattern_list_limits::max_fast_path_pathname_length;
12714
0
  using url_pattern_list_limits::max_fast_path_segments;
12715
0
  out = engine_result{};
12716
0
  if (ulen == 0 || url[0] != '/' || ulen > max_fast_path_pathname_length) {
12717
0
    out.within_fast_path = false;  // the sequential fallback decides
12718
0
    return;
12719
0
  }
12720
0
  const table_view t = view_of(r);
12721
0
  const route_record* routes = r.section<route_record>(r.routes_offset);
12722
0
12723
0
  // Segment scan; overflow means the input is beyond the fast path.
12724
0
  uint16_t soff[max_fast_path_segments + 1];
12725
0
  const uint32_t nseg = scan_segments(url, ulen, soff);
12726
0
  if (nseg == 0) {
12727
0
    out.within_fast_path = false;
12728
0
    return;
12729
0
  }
12730
0
  const auto seg_len = [&](uint32_t i) {
12731
0
    return static_cast<uint32_t>(soff[i + 1]) - soff[i] - 1u;
12732
0
  };
12733
0
  const auto seg_ref = [&](uint32_t i) {
12734
0
    const uint32_t off = soff[i];
12735
0
    return segment_ref{url + off, seg_len(i), ulen - off};
12736
0
  };
12737
0
12738
0
  // Trie walk with an explicit bounded backtrack stack. Every node offers
12739
0
  // the same three alternatives in the compiled priority order (static
12740
0
  // child, param child, wildcard); a backtrack record stores the resume
12741
0
  // point, and a record is pushed only when the node still has an untried
12742
0
  // alternative, so the stack never exceeds the walk depth. Edges encoded
12743
0
  // as -2 - route are pure-leaf shortcuts.
12744
0
  enum alternative : uint16_t {
12745
0
    alt_static = 0,
12746
0
    alt_param = 1,
12747
0
    alt_wild = 2,
12748
0
    alt_none = 3
12749
0
  };
12750
0
  struct backtrack {
12751
0
    int32_t node;
12752
0
    uint16_t i;
12753
0
    uint16_t resume;
12754
0
  };
12755
0
  backtrack stack[max_fast_path_segments + 1];
12756
0
  uint32_t sp = 0;
12757
0
  int32_t node = 0;
12758
0
  uint32_t i = 0;
12759
0
  uint32_t alt = alt_static;
12760
0
  int32_t route = -1;
12761
0
  uint32_t wild_from = 0;
12762
0
  bool wild = false;
12763
0
  for (;;) {
12764
0
    const node_record& nd = t.nodes[static_cast<size_t>(node)];
12765
0
    if (alt == alt_static) {
12766
0
      if (i == nseg) {
12767
0
        if (nd.terminal_route >= 0) {
12768
0
          route = nd.terminal_route;
12769
0
          break;
12770
0
        }
12771
0
        alt = alt_none;
12772
0
      } else {
12773
0
        const int32_t ord = dispatch_static(t, nd, seg_ref(i));
12774
0
        if (ord >= 0) {
12775
0
          const int32_t nxt = t.edges[static_cast<size_t>(nd.first_child) +
12776
0
                                      static_cast<size_t>(ord)]
12777
0
                                  .node;
12778
0
          if (nxt >= 0) {
12779
0
            if (nd.has_alternative) {
12780
0
              stack[sp++] = {node, static_cast<uint16_t>(i), alt_param};
12781
0
            }
12782
0
            node = nxt;
12783
0
            i++;
12784
0
            continue;
12785
0
          }
12786
0
          // Pure-leaf shortcut.
12787
0
          if (i + 1 == nseg) {
12788
0
            route = -2 - nxt;
12789
0
            break;
12790
0
          }
12791
0
          alt = alt_param;  // dead end: fall through to this node's fallbacks
12792
0
        } else {
12793
0
          alt = alt_param;
12794
0
        }
12795
0
      }
12796
0
    }
12797
0
    if (alt == alt_param) {
12798
0
      const int32_t pc = nd.param_child;
12799
0
      if (pc != -1 && seg_len(i) > 0) {
12800
0
        if (pc >= 0) {
12801
0
          if (nd.wild_route >= 0) {
12802
0
            stack[sp++] = {node, static_cast<uint16_t>(i), alt_wild};
12803
0
          }
12804
0
          node = pc;
12805
0
          i++;
12806
0
          alt = alt_static;
12807
0
          continue;
12808
0
        }
12809
0
        // Pure-leaf param shortcut.
12810
0
        if (i + 1 == nseg) {
12811
0
          route = -2 - pc;
12812
0
          break;
12813
0
        }
12814
0
        alt = alt_wild;  // only the wildcard remains here
12815
0
      } else {
12816
0
        alt = alt_wild;
12817
0
      }
12818
0
    }
12819
0
    if (alt == alt_wild) {
12820
0
      if (nd.wild_route >= 0 &&
12821
0
          wildcard_tail_ok(url + soff[i], ulen - soff[i])) {
12822
0
        route = nd.wild_route;
12823
0
        wild_from = i;
12824
0
        wild = true;
12825
0
        break;
12826
0
      }
12827
0
      // No alternative left at this node: resume from the backtrack stack
12828
0
      // (alt is overwritten by the popped record).
12829
0
    }
12830
0
    if (sp == 0) {
12831
0
      break;  // nothing to resume: miss
12832
0
    }
12833
0
    --sp;
12834
0
    node = stack[sp].node;
12835
0
    i = stack[sp].i;
12836
0
    alt = stack[sp].resume;
12837
0
  }
12838
0
12839
0
  out.route = route;
12840
0
  if (route >= 0) {  // capture extraction: params first, wildcard tail last
12841
0
    const route_record& rm = routes[static_cast<size_t>(route)];
12842
0
    uint32_t nc = 0;
12843
0
    for (uint32_t k = 0; k < rm.n_params; k++) {
12844
0
      const uint32_t p = rm.param_positions[k];
12845
0
      out.captures[nc++] = {soff[p], seg_len(p)};
12846
0
    }
12847
0
    if (wild) {
12848
0
      out.captures[nc++] = {soff[wild_from], ulen - soff[wild_from]};
12849
0
    }
12850
0
    out.capture_count = nc;
12851
0
  }
12852
0
}
12853
12854
}  // namespace ada::url_pattern_list_detail
12855
12856
namespace ada {
12857
12858
template <url_pattern_regex::regex_concept regex_provider>
12859
tl::expected<url_pattern_list<regex_provider>, errors>
12860
url_pattern_list<regex_provider>::create(
12861
    std::vector<std::string>&& pathname_patterns, bool ignore_case) {
12862
  url_pattern_list list{};
12863
  list.patterns_ = std::move(pathname_patterns);
12864
  auto compiled = url_pattern_list_detail::compile_pathname_patterns(
12865
      list.patterns_, ignore_case);
12866
  if (!compiled) {
12867
    return tl::unexpected(compiled.error());
12868
  }
12869
  list.compiled_ = std::move(*compiled);
12870
  return list;
12871
}
12872
12873
template <url_pattern_regex::regex_concept regex_provider>
12874
void url_pattern_list<regex_provider>::consider_route(
12875
    uint32_t route_index, std::string_view pathname, std::string_view probe,
12876
    bool fold_input, url_pattern_list_detail::engine_result& best,
12877
    std::vector<std::optional<std::string>>& best_groups) const {
12878
  namespace detail = url_pattern_list_detail;
12879
  const detail::route_record* routes =
12880
      compiled_.section<detail::route_record>(compiled_.routes_offset);
12881
  const detail::route_record& candidate = routes[route_index];
12882
  // Only test candidates that would outrank the current best.
12883
  if (best.route >= 0) {
12884
    const detail::route_record& current =
12885
        routes[static_cast<size_t>(best.route)];
12886
    if (!detail::outranks(candidate.kind_sequence, candidate.kind_length,
12887
                          route_index, current.kind_sequence,
12888
                          current.kind_length,
12889
                          static_cast<size_t>(best.route))) {
12890
      return;
12891
    }
12892
  }
12893
  if (candidate.mode == detail::route_mode::regexp) {
12894
    // A route whose certain segment shape does not fit the input cannot
12895
    // match at all: skip the provider.
12896
    if (!detail::match_regexp_shape(compiled_, route_index, probe,
12897
                                    fold_input)) {
12898
      return;
12899
    }
12900
    // URLPattern's own test/exec split: regex_match answers yes/no (this is
12901
    // what rejects a non-matching route cheaply; a regex_search miss can
12902
    // cost a scan over every start position), and regex_search then
12903
    // produces the group values exactly as url_pattern::exec does.
12904
    const url_pattern_component<regex_provider>& component =
12905
        regexp_components_[static_cast<size_t>(candidate.regexp_component)];
12906
    if (!component.fast_test(pathname)) {
12907
      return;
12908
    }
12909
    auto groups = component.fast_match(pathname);
12910
    if (groups) {
12911
      best = detail::engine_result{};
12912
      best.route = static_cast<int32_t>(route_index);
12913
      best_groups = std::move(*groups);
12914
    }
12915
  } else {
12916
    detail::engine_result scratch{};
12917
    if (detail::match_route_sequential(compiled_, route_index, probe,
12918
                                       fold_input, scratch)) {
12919
      scratch.route = static_cast<int32_t>(route_index);
12920
      best = scratch;
12921
      best_groups.clear();
12922
    }
12923
  }
12924
}
12925
12926
template <url_pattern_regex::regex_concept regex_provider>
12927
url_pattern_list_match_result url_pattern_list<regex_provider>::match(
12928
    std::string_view pathname) const {
12929
  namespace detail = url_pattern_list_detail;
12930
  using url_pattern_list_limits::max_fast_path_pathname_length;
12931
  // A default-constructed list (or one compiled from no routes) has no
12932
  // tables. Walking them would dereference section pointers into an empty
12933
  // arena.
12934
  if (patterns_.empty() || compiled_.arena.empty()) {
12935
    return {};
12936
  }
12937
  // With ignore_case the compiled literals are ASCII-folded, so the fast
12938
  // path probes a folded copy of the input (offsets are unchanged, so the
12939
  // captures still slice the original). Inputs beyond the fast-path length
12940
  // are folded on the fly by the sequential matcher instead.
12941
  char folded[max_fast_path_pathname_length];
12942
  std::string_view probe = pathname;
12943
  bool fold_input = false;
12944
  if (compiled_.ignore_case) {
12945
    if (pathname.size() <= max_fast_path_pathname_length) {
12946
      detail::ascii_fold(pathname.data(),
12947
                         static_cast<uint32_t>(pathname.size()), folded);
12948
      probe = std::string_view(folded, pathname.size());
12949
    } else {
12950
      fold_input = true;
12951
    }
12952
  }
12953
  detail::engine_result best{};
12954
  std::vector<std::optional<std::string>> best_groups{};
12955
  detail::match_compiled(compiled_, probe.data(),
12956
                         static_cast<uint32_t>(probe.size()), best);
12957
  const uint32_t* aux = compiled_.section<uint32_t>(compiled_.aux_offset);
12958
  if (!best.within_fast_path) {
12959
    // The input exceeds a fast-path limit (length, segment count, or no
12960
    // leading '/'): the sequential fallback matches every route with
12961
    // identical priority semantics.
12962
    best = detail::engine_result{};
12963
    for (size_t i = 0; i < patterns_.size(); i++) {
12964
      consider_route(static_cast<uint32_t>(i), pathname, probe, fold_input,
12965
                     best, best_groups);
12966
    }
12967
  } else if (best.route >= 0) {
12968
    // A fast-path winner is final except for the auxiliary routes recorded
12969
    // at creation as able to outrank it (usually none).
12970
    const detail::route_record& winner =
12971
        compiled_.section<detail::route_record>(
12972
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12973
    for (uint32_t k = 0; k < winner.challenger_count; k++) {
12974
      consider_route(aux[winner.challenger_first + k], pathname, probe,
12975
                     fold_input, best, best_groups);
12976
    }
12977
  } else {
12978
    // A fast-path miss: any auxiliary route may still match.
12979
    for (uint32_t k = 0; k < compiled_.n_aux_all; k++) {
12980
      consider_route(aux[k], pathname, probe, fold_input, best, best_groups);
12981
    }
12982
  }
12983
  url_pattern_list_match_result result{};
12984
  result.route_index = best.route;
12985
  if (best.route >= 0) {
12986
    const detail::route_record& winner =
12987
        compiled_.section<detail::route_record>(
12988
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12989
    if (winner.mode == detail::route_mode::regexp) {
12990
      result.regexp_route = true;
12991
      result.regexp_groups = std::move(best_groups);
12992
    } else {
12993
      result.capture_count = best.capture_count;
12994
      result.captures_truncated = best.captures_truncated;
12995
      result.captures = best.captures;
12996
    }
12997
  }
12998
  return result;
12999
}
13000
13001
}  // namespace ada
13002
#endif  // ADA_INCLUDE_URL_PATTERN
13003
#endif  // ADA_URL_PATTERN_LIST_INL_H
13004
/* end file include/ada/url_pattern_list-inl.h */
13005
13006
// Public API
13007
/* begin file include/ada/ada_version.h */
13008
/**
13009
 * @file ada_version.h
13010
 * @brief Definitions for Ada's version number.
13011
 */
13012
#ifndef ADA_ADA_VERSION_H
13013
#define ADA_ADA_VERSION_H
13014
13015
0
#define ADA_VERSION "4.0.0"
13016
13017
namespace ada {
13018
13019
enum {
13020
  ADA_VERSION_MAJOR = 4,
13021
  ADA_VERSION_MINOR = 0,
13022
  ADA_VERSION_REVISION = 0,
13023
};
13024
13025
}  // namespace ada
13026
13027
#endif  // ADA_ADA_VERSION_H
13028
/* end file include/ada/ada_version.h */
13029
/* begin file include/ada/implementation-inl.h */
13030
/**
13031
 * @file implementation-inl.h
13032
 */
13033
#ifndef ADA_IMPLEMENTATION_INL_H
13034
#define ADA_IMPLEMENTATION_INL_H
13035
13036
13037
13038
#include <cstddef>
13039
#include <span>
13040
#include <string>
13041
#include <string_view>
13042
#include <utility>
13043
#include <variant>
13044
#include <vector>
13045
13046
namespace ada {
13047
13048
#if ADA_INCLUDE_URL_PATTERN
13049
template <url_pattern_regex::regex_concept regex_provider>
13050
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
13051
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
13052
                  const std::string_view* base_url,
13053
                  const url_pattern_options* options) {
13054
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
13055
                                                        base_url, options);
13056
}
13057
13058
template <url_pattern_regex::regex_concept regex_provider>
13059
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13060
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
13061
                       const std::string_view* base_url,
13062
                       const url_pattern_options* options) {
13063
  std::vector<std::string> processed;
13064
  processed.reserve(pathname_patterns.size());
13065
  for (const std::string_view pattern : pathname_patterns) {
13066
    if (base_url == nullptr) {
13067
      processed.emplace_back(pattern);
13068
      continue;
13069
    }
13070
    // With a base URL, each pattern is processed exactly as the pathname of
13071
    // a URLPatternInit would be: a relative pattern is resolved against the
13072
    // base URL's path (and an unparsable base URL is a type error).
13073
    url_pattern_init init{};
13074
    init.pathname = std::string(pattern);
13075
    init.base_url = std::string(*base_url);
13076
    auto result = url_pattern_init::process(
13077
        init, url_pattern_init::process_type::pattern);
13078
    if (!result) {
13079
      return tl::unexpected(result.error());
13080
    }
13081
    processed.push_back(std::move(result->pathname).value_or(std::string{}));
13082
  }
13083
  const bool ignore_case = options != nullptr && options->ignore_case;
13084
  auto list = url_pattern_list<regex_provider>::create(std::move(processed),
13085
                                                       ignore_case);
13086
  if (!list) {
13087
    return list;
13088
  }
13089
  // Routes outside the static/":param"/"*" subset are compiled as a
13090
  // URLPattern pathname component through the provider, with the same
13091
  // options parse_url_pattern would use (ignore_case included).
13092
  auto compile_options = url_pattern_compile_component_options::PATHNAME;
13093
  compile_options.ignore_case = ignore_case;
13094
  const url_pattern_list_detail::route_record* routes =
13095
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13096
          list->compiled_.routes_offset);
13097
  for (size_t i = 0; i < list->patterns_.size(); i++) {
13098
    if (routes[i].regexp_component < 0) {
13099
      continue;
13100
    }
13101
    auto component = url_pattern_component<regex_provider>::compile(
13102
        list->patterns_[i], url_pattern_helpers::canonicalize_pathname,
13103
        compile_options);
13104
    if (!component) {
13105
      return tl::unexpected(component.error());
13106
    }
13107
    list->compiled_.group_names[i] = component->group_name_list;
13108
    list->regexp_components_.push_back(std::move(*component));
13109
  }
13110
  return list;
13111
}
13112
13113
template <url_pattern_regex::regex_concept regex_provider>
13114
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13115
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns) {
13116
  std::vector<std::string> texts;
13117
  texts.reserve(patterns.size());
13118
  const bool ignore_case = !patterns.empty() && patterns[0].ignore_case();
13119
  for (const url_pattern<regex_provider>& pattern : patterns) {
13120
    if (pattern.ignore_case() != ignore_case) {
13121
      return tl::unexpected(errors::type_error);  // one flag per list
13122
    }
13123
    texts.emplace_back(pattern.get_pathname());
13124
  }
13125
  auto list =
13126
      url_pattern_list<regex_provider>::create(std::move(texts), ignore_case);
13127
  if (!list) {
13128
    return list;
13129
  }
13130
  // Routes outside the subset reuse the pattern's already compiled pathname
13131
  // component: no second create_instance through the provider.
13132
  const url_pattern_list_detail::route_record* routes =
13133
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13134
          list->compiled_.routes_offset);
13135
  for (size_t i = 0; i < patterns.size(); i++) {
13136
    if (routes[i].regexp_component < 0) {
13137
      continue;
13138
    }
13139
    list->compiled_.group_names[i] =
13140
        patterns[i].pathname_component.group_name_list;
13141
    list->regexp_components_.push_back(patterns[i].pathname_component);
13142
  }
13143
  return list;
13144
}
13145
#endif  // ADA_INCLUDE_URL_PATTERN
13146
13147
}  // namespace ada
13148
13149
#endif  // ADA_IMPLEMENTATION_INL_H
13150
/* end file include/ada/implementation-inl.h */
13151
13152
#endif  // ADA_H
13153
/* end file include/ada.h */