Coverage Report

Created: 2026-11-12 06:57

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-11-07 22:18:56 -0500. 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 (the default), NEON all-zero tests use an integer compare. Define
546
// it to 0 to reinterpret the bits as a double and compare against 0.0: that
547
// avoids a GPR transfer but is incorrect if flush-to-zero is enabled (e.g.,
548
// when the application is built with -ffast-math).
549
#ifndef ADA_NEON_SAFE_ZERO_CHECK
550
#define ADA_NEON_SAFE_ZERO_CHECK 1
551
#endif  // ADA_NEON_SAFE_ZERO_CHECK
552
553
#endif  // ADA_COMMON_DEFS_H
554
/* end file include/ada/common_defs.h */
555
#include <cstdint>
556
557
/**
558
 * These functions are not part of our public API and may
559
 * change at any time.
560
 * @private
561
 * @namespace ada::character_sets
562
 * @brief Includes the definitions for unicode character sets.
563
 */
564
namespace ada::character_sets {
565
ada_really_inline constexpr bool bit_at(const uint8_t a[], uint8_t i);
566
}  // namespace ada::character_sets
567
568
#endif  // ADA_CHARACTER_SETS_H
569
/* end file include/ada/character_sets.h */
570
/* begin file include/ada/character_sets-inl.h */
571
/**
572
 * @file character_sets-inl.h
573
 * @brief Definitions of the character sets used by unicode functions.
574
 * @author Node.js
575
 * @see https://github.com/nodejs/node/blob/main/src/node_url_tables.cc
576
 */
577
#ifndef ADA_CHARACTER_SETS_INL_H
578
#define ADA_CHARACTER_SETS_INL_H
579
580
581
#include <cstdint>
582
583
/**
584
 * These functions are not part of our public API and may
585
 * change at any time.
586
 * @private
587
 */
588
namespace ada::character_sets {
589
590
constexpr char hex[1024] =
591
    "%00\0%01\0%02\0%03\0%04\0%05\0%06\0%07\0"
592
    "%08\0%09\0%0A\0%0B\0%0C\0%0D\0%0E\0%0F\0"
593
    "%10\0%11\0%12\0%13\0%14\0%15\0%16\0%17\0"
594
    "%18\0%19\0%1A\0%1B\0%1C\0%1D\0%1E\0%1F\0"
595
    "%20\0%21\0%22\0%23\0%24\0%25\0%26\0%27\0"
596
    "%28\0%29\0%2A\0%2B\0%2C\0%2D\0%2E\0%2F\0"
597
    "%30\0%31\0%32\0%33\0%34\0%35\0%36\0%37\0"
598
    "%38\0%39\0%3A\0%3B\0%3C\0%3D\0%3E\0%3F\0"
599
    "%40\0%41\0%42\0%43\0%44\0%45\0%46\0%47\0"
600
    "%48\0%49\0%4A\0%4B\0%4C\0%4D\0%4E\0%4F\0"
601
    "%50\0%51\0%52\0%53\0%54\0%55\0%56\0%57\0"
602
    "%58\0%59\0%5A\0%5B\0%5C\0%5D\0%5E\0%5F\0"
603
    "%60\0%61\0%62\0%63\0%64\0%65\0%66\0%67\0"
604
    "%68\0%69\0%6A\0%6B\0%6C\0%6D\0%6E\0%6F\0"
605
    "%70\0%71\0%72\0%73\0%74\0%75\0%76\0%77\0"
606
    "%78\0%79\0%7A\0%7B\0%7C\0%7D\0%7E\0%7F\0"
607
    "%80\0%81\0%82\0%83\0%84\0%85\0%86\0%87\0"
608
    "%88\0%89\0%8A\0%8B\0%8C\0%8D\0%8E\0%8F\0"
609
    "%90\0%91\0%92\0%93\0%94\0%95\0%96\0%97\0"
610
    "%98\0%99\0%9A\0%9B\0%9C\0%9D\0%9E\0%9F\0"
611
    "%A0\0%A1\0%A2\0%A3\0%A4\0%A5\0%A6\0%A7\0"
612
    "%A8\0%A9\0%AA\0%AB\0%AC\0%AD\0%AE\0%AF\0"
613
    "%B0\0%B1\0%B2\0%B3\0%B4\0%B5\0%B6\0%B7\0"
614
    "%B8\0%B9\0%BA\0%BB\0%BC\0%BD\0%BE\0%BF\0"
615
    "%C0\0%C1\0%C2\0%C3\0%C4\0%C5\0%C6\0%C7\0"
616
    "%C8\0%C9\0%CA\0%CB\0%CC\0%CD\0%CE\0%CF\0"
617
    "%D0\0%D1\0%D2\0%D3\0%D4\0%D5\0%D6\0%D7\0"
618
    "%D8\0%D9\0%DA\0%DB\0%DC\0%DD\0%DE\0%DF\0"
619
    "%E0\0%E1\0%E2\0%E3\0%E4\0%E5\0%E6\0%E7\0"
620
    "%E8\0%E9\0%EA\0%EB\0%EC\0%ED\0%EE\0%EF\0"
621
    "%F0\0%F1\0%F2\0%F3\0%F4\0%F5\0%F6\0%F7\0"
622
    "%F8\0%F9\0%FA\0%FB\0%FC\0%FD\0%FE\0%FF";
623
624
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32] = {
625
    // 00     01     02     03     04     05     06     07
626
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
627
    // 08     09     0A     0B     0C     0D     0E     0F
628
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
629
    // 10     11     12     13     14     15     16     17
630
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
631
    // 18     19     1A     1B     1C     1D     1E     1F
632
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
633
    // 20     21     22     23     24     25     26     27
634
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
635
    // 28     29     2A     2B     2C     2D     2E     2F
636
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
637
    // 30     31     32     33     34     35     36     37
638
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
639
    // 38     39     3A     3B     3C     3D     3E     3F
640
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
641
    // 40     41     42     43     44     45     46     47
642
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
643
    // 48     49     4A     4B     4C     4D     4E     4F
644
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
645
    // 50     51     52     53     54     55     56     57
646
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
647
    // 58     59     5A     5B     5C     5D     5E     5F
648
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
649
    // 60     61     62     63     64     65     66     67
650
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
651
    // 68     69     6A     6B     6C     6D     6E     6F
652
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
653
    // 70     71     72     73     74     75     76     77
654
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
655
    // 78     79     7A     7B     7C     7D     7E     7F
656
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
657
    // 80     81     82     83     84     85     86     87
658
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
659
    // 88     89     8A     8B     8C     8D     8E     8F
660
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
661
    // 90     91     92     93     94     95     96     97
662
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
663
    // 98     99     9A     9B     9C     9D     9E     9F
664
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
665
    // A0     A1     A2     A3     A4     A5     A6     A7
666
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
667
    // A8     A9     AA     AB     AC     AD     AE     AF
668
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
669
    // B0     B1     B2     B3     B4     B5     B6     B7
670
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
671
    // B8     B9     BA     BB     BC     BD     BE     BF
672
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
673
    // C0     C1     C2     C3     C4     C5     C6     C7
674
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
675
    // C8     C9     CA     CB     CC     CD     CE     CF
676
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
677
    // D0     D1     D2     D3     D4     D5     D6     D7
678
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
679
    // D8     D9     DA     DB     DC     DD     DE     DF
680
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
681
    // E0     E1     E2     E3     E4     E5     E6     E7
682
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
683
    // E8     E9     EA     EB     EC     ED     EE     EF
684
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
685
    // F0     F1     F2     F3     F4     F5     F6     F7
686
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
687
    // F8     F9     FA     FB     FC     FD     FE     FF
688
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
689
690
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32] = {
691
    // 00     01     02     03     04     05     06     07
692
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
693
    // 08     09     0A     0B     0C     0D     0E     0F
694
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
695
    // 10     11     12     13     14     15     16     17
696
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
697
    // 18     19     1A     1B     1C     1D     1E     1F
698
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
699
    // 20     21     22     23     24     25     26     27
700
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x80,
701
    // 28     29     2A     2B     2C     2D     2E     2F
702
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
703
    // 30     31     32     33     34     35     36     37
704
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
705
    // 38     39     3A     3B     3C     3D     3E     3F
706
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
707
    // 40     41     42     43     44     45     46     47
708
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
709
    // 48     49     4A     4B     4C     4D     4E     4F
710
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
711
    // 50     51     52     53     54     55     56     57
712
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
713
    // 58     59     5A     5B     5C     5D     5E     5F
714
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
715
    // 60     61     62     63     64     65     66     67
716
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
717
    // 68     69     6A     6B     6C     6D     6E     6F
718
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
719
    // 70     71     72     73     74     75     76     77
720
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
721
    // 78     79     7A     7B     7C     7D     7E     7F
722
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
723
    // 80     81     82     83     84     85     86     87
724
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
725
    // 88     89     8A     8B     8C     8D     8E     8F
726
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
727
    // 90     91     92     93     94     95     96     97
728
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
729
    // 98     99     9A     9B     9C     9D     9E     9F
730
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
731
    // A0     A1     A2     A3     A4     A5     A6     A7
732
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
733
    // A8     A9     AA     AB     AC     AD     AE     AF
734
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
735
    // B0     B1     B2     B3     B4     B5     B6     B7
736
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
737
    // B8     B9     BA     BB     BC     BD     BE     BF
738
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
739
    // C0     C1     C2     C3     C4     C5     C6     C7
740
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
741
    // C8     C9     CA     CB     CC     CD     CE     CF
742
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
743
    // D0     D1     D2     D3     D4     D5     D6     D7
744
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
745
    // D8     D9     DA     DB     DC     DD     DE     DF
746
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
747
    // E0     E1     E2     E3     E4     E5     E6     E7
748
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
749
    // E8     E9     EA     EB     EC     ED     EE     EF
750
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
751
    // F0     F1     F2     F3     F4     F5     F6     F7
752
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
753
    // F8     F9     FA     FB     FC     FD     FE     FF
754
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
755
756
constexpr uint8_t QUERY_PERCENT_ENCODE[32] = {
757
    // 00     01     02     03     04     05     06     07
758
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
759
    // 08     09     0A     0B     0C     0D     0E     0F
760
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
761
    // 10     11     12     13     14     15     16     17
762
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
763
    // 18     19     1A     1B     1C     1D     1E     1F
764
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
765
    // 20     21     22     23     24     25     26     27
766
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
767
    // 28     29     2A     2B     2C     2D     2E     2F
768
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
769
    // 30     31     32     33     34     35     36     37
770
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
771
    // 38     39     3A     3B     3C     3D     3E     3F
772
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
773
    // 40     41     42     43     44     45     46     47
774
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
775
    // 48     49     4A     4B     4C     4D     4E     4F
776
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
777
    // 50     51     52     53     54     55     56     57
778
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
779
    // 58     59     5A     5B     5C     5D     5E     5F
780
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
781
    // 60     61     62     63     64     65     66     67
782
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
783
    // 68     69     6A     6B     6C     6D     6E     6F
784
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
785
    // 70     71     72     73     74     75     76     77
786
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
787
    // 78     79     7A     7B     7C     7D     7E     7F
788
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
789
    // 80     81     82     83     84     85     86     87
790
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
791
    // 88     89     8A     8B     8C     8D     8E     8F
792
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
793
    // 90     91     92     93     94     95     96     97
794
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
795
    // 98     99     9A     9B     9C     9D     9E     9F
796
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
797
    // A0     A1     A2     A3     A4     A5     A6     A7
798
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
799
    // A8     A9     AA     AB     AC     AD     AE     AF
800
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
801
    // B0     B1     B2     B3     B4     B5     B6     B7
802
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
803
    // B8     B9     BA     BB     BC     BD     BE     BF
804
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
805
    // C0     C1     C2     C3     C4     C5     C6     C7
806
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
807
    // C8     C9     CA     CB     CC     CD     CE     CF
808
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
809
    // D0     D1     D2     D3     D4     D5     D6     D7
810
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
811
    // D8     D9     DA     DB     DC     DD     DE     DF
812
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
813
    // E0     E1     E2     E3     E4     E5     E6     E7
814
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
815
    // E8     E9     EA     EB     EC     ED     EE     EF
816
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
817
    // F0     F1     F2     F3     F4     F5     F6     F7
818
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
819
    // F8     F9     FA     FB     FC     FD     FE     FF
820
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
821
822
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32] = {
823
    // 00     01     02     03     04     05     06     07
824
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
825
    // 08     09     0A     0B     0C     0D     0E     0F
826
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
827
    // 10     11     12     13     14     15     16     17
828
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
829
    // 18     19     1A     1B     1C     1D     1E     1F
830
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
831
    // 20     21     22     23     24     25     26     27
832
    0x01 | 0x00 | 0x04 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
833
    // 28     29     2A     2B     2C     2D     2E     2F
834
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
835
    // 30     31     32     33     34     35     36     37
836
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
837
    // 38     39     3A     3B     3C     3D     3E     3F
838
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00,
839
    // 40     41     42     43     44     45     46     47
840
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
841
    // 48     49     4A     4B     4C     4D     4E     4F
842
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
843
    // 50     51     52     53     54     55     56     57
844
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
845
    // 58     59     5A     5B     5C     5D     5E     5F
846
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
847
    // 60     61     62     63     64     65     66     67
848
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
849
    // 68     69     6A     6B     6C     6D     6E     6F
850
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
851
    // 70     71     72     73     74     75     76     77
852
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
853
    // 78     79     7A     7B     7C     7D     7E     7F
854
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
855
    // 80     81     82     83     84     85     86     87
856
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
857
    // 88     89     8A     8B     8C     8D     8E     8F
858
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
859
    // 90     91     92     93     94     95     96     97
860
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
861
    // 98     99     9A     9B     9C     9D     9E     9F
862
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
863
    // A0     A1     A2     A3     A4     A5     A6     A7
864
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
865
    // A8     A9     AA     AB     AC     AD     AE     AF
866
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
867
    // B0     B1     B2     B3     B4     B5     B6     B7
868
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
869
    // B8     B9     BA     BB     BC     BD     BE     BF
870
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
871
    // C0     C1     C2     C3     C4     C5     C6     C7
872
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
873
    // C8     C9     CA     CB     CC     CD     CE     CF
874
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
875
    // D0     D1     D2     D3     D4     D5     D6     D7
876
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
877
    // D8     D9     DA     DB     DC     DD     DE     DF
878
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
879
    // E0     E1     E2     E3     E4     E5     E6     E7
880
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
881
    // E8     E9     EA     EB     EC     ED     EE     EF
882
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
883
    // F0     F1     F2     F3     F4     F5     F6     F7
884
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
885
    // F8     F9     FA     FB     FC     FD     FE     FF
886
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
887
888
constexpr uint8_t USERINFO_PERCENT_ENCODE[32] = {
889
    // 00     01     02     03     04     05     06     07
890
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
891
    // 08     09     0A     0B     0C     0D     0E     0F
892
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
893
    // 10     11     12     13     14     15     16     17
894
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
895
    // 18     19     1A     1B     1C     1D     1E     1F
896
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
897
    // 20     21     22     23     24     25     26     27
898
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
899
    // 28     29     2A     2B     2C     2D     2E     2F
900
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80,
901
    // 30     31     32     33     34     35     36     37
902
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
903
    // 38     39     3A     3B     3C     3D     3E     3F
904
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
905
    // 40     41     42     43     44     45     46     47
906
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
907
    // 48     49     4A     4B     4C     4D     4E     4F
908
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
909
    // 50     51     52     53     54     55     56     57
910
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
911
    // 58     59     5A     5B     5C     5D     5E     5F
912
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
913
    // 60     61     62     63     64     65     66     67
914
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
915
    // 68     69     6A     6B     6C     6D     6E     6F
916
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
917
    // 70     71     72     73     74     75     76     77
918
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
919
    // 78     79     7A     7B     7C     7D     7E     7F
920
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80,
921
    // 80     81     82     83     84     85     86     87
922
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
923
    // 88     89     8A     8B     8C     8D     8E     8F
924
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
925
    // 90     91     92     93     94     95     96     97
926
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
927
    // 98     99     9A     9B     9C     9D     9E     9F
928
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
929
    // A0     A1     A2     A3     A4     A5     A6     A7
930
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
931
    // A8     A9     AA     AB     AC     AD     AE     AF
932
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
933
    // B0     B1     B2     B3     B4     B5     B6     B7
934
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
935
    // B8     B9     BA     BB     BC     BD     BE     BF
936
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
937
    // C0     C1     C2     C3     C4     C5     C6     C7
938
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
939
    // C8     C9     CA     CB     CC     CD     CE     CF
940
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
941
    // D0     D1     D2     D3     D4     D5     D6     D7
942
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
943
    // D8     D9     DA     DB     DC     DD     DE     DF
944
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
945
    // E0     E1     E2     E3     E4     E5     E6     E7
946
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
947
    // E8     E9     EA     EB     EC     ED     EE     EF
948
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
949
    // F0     F1     F2     F3     F4     F5     F6     F7
950
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
951
    // F8     F9     FA     FB     FC     FD     FE     FF
952
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
953
954
constexpr uint8_t PATH_PERCENT_ENCODE[32] = {
955
    // 00     01     02     03     04     05     06     07
956
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
957
    // 08     09     0A     0B     0C     0D     0E     0F
958
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
959
    // 10     11     12     13     14     15     16     17
960
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
961
    // 18     19     1A     1B     1C     1D     1E     1F
962
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
963
    // 20     21     22     23     24     25     26     27
964
    0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00,
965
    // 28     29     2A     2B     2C     2D     2E     2F
966
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
967
    // 30     31     32     33     34     35     36     37
968
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
969
    // 38     39     3A     3B     3C     3D     3E     3F
970
    0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80,
971
    // 40     41     42     43     44     45     46     47
972
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
973
    // 48     49     4A     4B     4C     4D     4E     4F
974
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
975
    // 50     51     52     53     54     55     56     57
976
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
977
    // 58     59     5A     5B     5C     5D     5E     5F
978
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x40 | 0x00,
979
    // 60     61     62     63     64     65     66     67
980
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
981
    // 68     69     6A     6B     6C     6D     6E     6F
982
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
983
    // 70     71     72     73     74     75     76     77
984
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
985
    // 78     79     7A     7B     7C     7D     7E     7F
986
    0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80,
987
    // 80     81     82     83     84     85     86     87
988
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
989
    // 88     89     8A     8B     8C     8D     8E     8F
990
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
991
    // 90     91     92     93     94     95     96     97
992
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
993
    // 98     99     9A     9B     9C     9D     9E     9F
994
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
995
    // A0     A1     A2     A3     A4     A5     A6     A7
996
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
997
    // A8     A9     AA     AB     AC     AD     AE     AF
998
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
999
    // B0     B1     B2     B3     B4     B5     B6     B7
1000
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1001
    // B8     B9     BA     BB     BC     BD     BE     BF
1002
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1003
    // C0     C1     C2     C3     C4     C5     C6     C7
1004
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1005
    // C8     C9     CA     CB     CC     CD     CE     CF
1006
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1007
    // D0     D1     D2     D3     D4     D5     D6     D7
1008
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1009
    // D8     D9     DA     DB     DC     DD     DE     DF
1010
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1011
    // E0     E1     E2     E3     E4     E5     E6     E7
1012
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1013
    // E8     E9     EA     EB     EC     ED     EE     EF
1014
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1015
    // F0     F1     F2     F3     F4     F5     F6     F7
1016
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1017
    // F8     F9     FA     FB     FC     FD     FE     FF
1018
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1019
1020
constexpr uint8_t WWW_FORM_URLENCODED_PERCENT_ENCODE[32] = {
1021
    // 00     01     02     03     04     05     06     07
1022
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1023
    // 08     09     0A     0B     0C     0D     0E     0F
1024
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1025
    // 10     11     12     13     14     15     16     17
1026
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1027
    // 18     19     1A     1B     1C     1D     1E     1F
1028
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1029
    // 20     21     22     23     24     25     26     27
1030
    0x00 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1031
    // 28     29     2A     2B     2C     2D     2E     2F
1032
    0x01 | 0x02 | 0x00 | 0x08 | 0x10 | 0x00 | 0x00 | 0x80,
1033
    // 30     31     32     33     34     35     36     37
1034
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1035
    // 38     39     3A     3B     3C     3D     3E     3F
1036
    0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1037
    // 40     41     42     43     44     45     46     47
1038
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1039
    // 48     49     4A     4B     4C     4D     4E     4F
1040
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1041
    // 50     51     52     53     54     55     56     57
1042
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1043
    // 58     59     5A     5B     5C     5D     5E     5F
1044
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
1045
    // 60     61     62     63     64     65     66     67
1046
    0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1047
    // 68     69     6A     6B     6C     6D     6E     6F
1048
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1049
    // 70     71     72     73     74     75     76     77
1050
    0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
1051
    // 78     79     7A     7B     7C     7D     7E     7F
1052
    0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1053
    // 80     81     82     83     84     85     86     87
1054
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1055
    // 88     89     8A     8B     8C     8D     8E     8F
1056
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1057
    // 90     91     92     93     94     95     96     97
1058
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1059
    // 98     99     9A     9B     9C     9D     9E     9F
1060
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1061
    // A0     A1     A2     A3     A4     A5     A6     A7
1062
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1063
    // A8     A9     AA     AB     AC     AD     AE     AF
1064
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1065
    // B0     B1     B2     B3     B4     B5     B6     B7
1066
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1067
    // B8     B9     BA     BB     BC     BD     BE     BF
1068
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1069
    // C0     C1     C2     C3     C4     C5     C6     C7
1070
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1071
    // C8     C9     CA     CB     CC     CD     CE     CF
1072
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1073
    // D0     D1     D2     D3     D4     D5     D6     D7
1074
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1075
    // D8     D9     DA     DB     DC     DD     DE     DF
1076
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1077
    // E0     E1     E2     E3     E4     E5     E6     E7
1078
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1079
    // E8     E9     EA     EB     EC     ED     EE     EF
1080
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1081
    // F0     F1     F2     F3     F4     F5     F6     F7
1082
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
1083
    // F8     F9     FA     FB     FC     FD     FE     FF
1084
    0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80};
1085
1086
1.44M
ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
1087
1.44M
  return !!(a[i >> 3] & (1 << (i & 7)));
1088
1.44M
}
1089
1090
}  // namespace ada::character_sets
1091
1092
#endif  // ADA_CHARACTER_SETS_INL_H
1093
/* end file include/ada/character_sets-inl.h */
1094
/* begin file include/ada/checkers-inl.h */
1095
/**
1096
 * @file checkers-inl.h
1097
 * @brief Definitions for URL specific checkers used within Ada.
1098
 */
1099
#ifndef ADA_CHECKERS_INL_H
1100
#define ADA_CHECKERS_INL_H
1101
1102
#include <bit>
1103
#include <cstddef>
1104
#include <cstdint>
1105
#include <string_view>
1106
/* begin file include/ada/checkers.h */
1107
/**
1108
 * @file checkers.h
1109
 * @brief Declarations for URL specific checkers used within Ada.
1110
 */
1111
#ifndef ADA_CHECKERS_H
1112
#define ADA_CHECKERS_H
1113
1114
1115
#include <cstdint>
1116
#include <cstring>
1117
#include <string_view>
1118
1119
/**
1120
 * These functions are not part of our public API and may
1121
 * change at any time.
1122
 * @private
1123
 * @namespace ada::checkers
1124
 * @brief Includes the definitions for validation functions
1125
 */
1126
namespace ada::checkers {
1127
1128
/**
1129
 * @private
1130
 * Assuming that x is an ASCII letter, this function returns the lower case
1131
 * equivalent.
1132
 * @details More likely to be inlined by the compiler and constexpr.
1133
 */
1134
constexpr char to_lower(char x) noexcept;
1135
1136
/**
1137
 * @private
1138
 * Returns true if the character is an ASCII letter. Equivalent to std::isalpha
1139
 * but more likely to be inlined by the compiler.
1140
 *
1141
 * @attention std::isalpha is not constexpr generally.
1142
 */
1143
constexpr bool is_alpha(char x) noexcept;
1144
1145
/**
1146
 * @private
1147
 * Check whether x is an ASCII digit. More likely to be inlined than
1148
 * std::isdigit.
1149
 */
1150
constexpr bool is_digit(char x) noexcept;
1151
1152
/**
1153
 * @private
1154
 * @details A string starts with a Windows drive letter if all of the following
1155
 * are true:
1156
 *
1157
 *   - its length is greater than or equal to 2
1158
 *   - its first two code points are a Windows drive letter
1159
 *   - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F
1160
 * (?), or U+0023 (#).
1161
 *
1162
 * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1163
 */
1164
inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept;
1165
1166
/**
1167
 * @private
1168
 * @details A normalized Windows drive letter is a Windows drive letter of which
1169
 * the second code point is U+003A (:).
1170
 */
1171
inline constexpr bool is_normalized_windows_drive_letter(
1172
    std::string_view input) noexcept;
1173
1174
/**
1175
 * @private
1176
 * Returns true if an input is an ipv4 address. It is assumed that the string
1177
 * does not contain uppercase ASCII characters (the input should have been
1178
 * lowered cased before calling this function) and is not empty.
1179
 */
1180
ada_really_inline constexpr bool is_ipv4(std::string_view view) noexcept;
1181
1182
/**
1183
 * @private
1184
 * Cheap pre-check for the WHATWG "ends in a number" / IPv4-host cases.
1185
 * False means the host cannot be IPv4: the last label is empty, does not
1186
 * start with an ASCII digit, or contains a character other than a hex
1187
 * digit or x/X. True means the slower IPv4 parsers should run.
1188
 *
1189
 * Safe on mixed-case input (unlike is_ipv4, which expects a lowercased
1190
 * host).
1191
 */
1192
ada_really_inline constexpr bool last_label_may_be_a_number(
1193
    std::string_view view) noexcept;
1194
1195
/**
1196
 * @private
1197
 * Returns a bitset. If the first bit is set, then at least one character needs
1198
 * percent encoding. If the second bit is set, a \\ is found. If the third bit
1199
 * is set then we have a dot. If the fourth bit is set, then we have a percent
1200
 * character.
1201
 */
1202
ada_really_inline constexpr uint8_t path_signature(
1203
    std::string_view input) noexcept;
1204
1205
/**
1206
 * @private
1207
 * Returns true if the length of the domain name and its labels are according to
1208
 * the specifications. The length of the domain must be 255 octets (253
1209
 * characters not including the last 2 which are the empty label reserved at the
1210
 * end). When the empty label is included (a dot at the end), the domain name
1211
 * can have 254 characters. The length of a label must be at least 1 and at most
1212
 * 63 characters.
1213
 * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034
1214
 * @see https://www.unicode.org/reports/tr46/#ToASCII
1215
 */
1216
ada_really_inline constexpr bool verify_dns_length(
1217
    std::string_view input) noexcept;
1218
1219
/**
1220
 * @private
1221
 * Fast-path parser for pure decimal IPv4 addresses (e.g., "192.168.1.1").
1222
 * Returns the packed 32-bit IPv4 address on success, or a value > 0xFFFFFFFF
1223
 * to indicate failure (caller should fall back to general parser).
1224
 * This is optimized for the common case where the input is a well-formed
1225
 * decimal IPv4 address with exactly 4 octets.
1226
 */
1227
ada_really_inline uint64_t try_parse_ipv4_fast(std::string_view input) noexcept;
1228
1229
/**
1230
 * Sentinel value indicating try_parse_ipv4_fast() did not succeed.
1231
 * Any value > 0xFFFFFFFF indicates the fast path should not be used.
1232
 */
1233
constexpr uint64_t ipv4_fast_fail = uint64_t(1) << 32;
1234
1235
}  // namespace ada::checkers
1236
1237
#endif  // ADA_CHECKERS_H
1238
/* end file include/ada/checkers.h */
1239
1240
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1241
#include <immintrin.h>
1242
#endif
1243
1244
namespace ada::checkers {
1245
1246
242k
constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
1247
1248
1.89M
constexpr bool is_ipv4_number_char(char x) noexcept {
1249
1.89M
  const unsigned char c = static_cast<unsigned char>(x);
1250
1.89M
  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1251
915k
         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
1252
1.89M
}
1253
1254
926k
constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
1255
926k
  if (view.empty()) {
1256
0
    return false;
1257
0
  }
1258
926k
  const char* start = view.data();
1259
926k
  const char* end = start + view.size();
1260
926k
  if (end[-1] == '.') {
1261
26.8k
    --end;
1262
26.8k
    if (end == start) {
1263
17.1k
      return false;
1264
17.1k
    }
1265
26.8k
  }
1266
909k
  if (!is_ipv4_number_char(end[-1])) {
1267
533k
    return false;
1268
533k
  }
1269
375k
  const char* label = end;
1270
1.05M
  while (label != start && is_ipv4_number_char(label[-1])) {
1271
674k
    --label;
1272
674k
  }
1273
375k
  if (label != start && label[-1] != '.') {
1274
291k
    return false;
1275
291k
  }
1276
84.6k
  return label != end && is_digit(*label);
1277
375k
}
1278
1279
1.54M
constexpr char to_lower(char x) noexcept { return (x | 0x20); }
1280
1281
789k
constexpr bool is_alpha(char x) noexcept {
1282
789k
  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
1283
789k
}
1284
1285
0
constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
1286
0
  return input.size() >= 2 &&
1287
0
         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
1288
0
         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
1289
0
                                  input[2] == '?' || input[2] == '#'));
1290
0
}
1291
1292
constexpr bool is_normalized_windows_drive_letter(
1293
0
    std::string_view input) noexcept {
1294
0
  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
1295
0
}
1296
1297
namespace detail {
1298
1299
// Unrolled pure-decimal IPv4. The common portable path for 7-16 byte hosts.
1300
ada_really_inline uint64_t
1301
80.5k
parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
1302
80.5k
  uint32_t ipv4 = 0;
1303
100k
  for (int i = 0; i < 4; ++i) {
1304
96.5k
    if (p == pend) [[unlikely]] {
1305
87
      return ipv4_fast_fail;
1306
87
    }
1307
96.4k
    uint32_t val;
1308
96.4k
    char c = *p;
1309
96.4k
    if (c >= '0' && c <= '9') [[likely]] {
1310
44.4k
      val = static_cast<uint32_t>(c - '0');
1311
44.4k
      ++p;
1312
52.0k
    } else {
1313
52.0k
      return ipv4_fast_fail;
1314
52.0k
    }
1315
44.4k
    if (p < pend) {
1316
43.7k
      c = *p;
1317
43.7k
      if (c >= '0' && c <= '9') {
1318
19.3k
        if (val == 0) [[unlikely]] {
1319
4.53k
          return ipv4_fast_fail;
1320
4.53k
        }
1321
14.7k
        val = val * 10u + static_cast<uint32_t>(c - '0');
1322
14.7k
        ++p;
1323
14.7k
        if (p < pend) {
1324
14.3k
          c = *p;
1325
14.3k
          if (c >= '0' && c <= '9') {
1326
10.8k
            val = val * 10u + static_cast<uint32_t>(c - '0');
1327
10.8k
            ++p;
1328
10.8k
            if (val > 255u) [[unlikely]] {
1329
6.78k
              return ipv4_fast_fail;
1330
6.78k
            }
1331
10.8k
          }
1332
14.3k
        }
1333
14.7k
      }
1334
43.7k
    }
1335
33.1k
    ipv4 = (ipv4 << 8) | val;
1336
33.1k
    if (i < 3) {
1337
29.7k
      if (p == pend || *p != '.') [[unlikely]] {
1338
13.6k
        return ipv4_fast_fail;
1339
13.6k
      }
1340
16.0k
      ++p;
1341
16.0k
    }
1342
33.1k
  }
1343
3.42k
  if (p != pend) {
1344
2.69k
    if (p == pend - 1 && *p == '.') {
1345
320
      return ipv4;
1346
320
    }
1347
2.37k
    return ipv4_fast_fail;
1348
2.69k
  }
1349
731
  return ipv4;
1350
3.42k
}
1351
1352
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1353
// Table-free AVX-512VL IPv4 parse (simdip parse_ipv4_avx512vl_notab5).
1354
// Needs VBMI2 for vpcompressb; ADA_AVX512 stays BW+VL (IPv6 does not use
1355
// VBMI2). Masked load of exactly `len` bytes (no over-read). Digit
1356
// placement is computed from compressed delimiter positions; octet > 255
1357
// is a dword compare on the zero-padded reversed digit group, in parallel
1358
// with the convert. Unusual-but-valid forms (octal, hex, leading zeros,
1359
// fewer than four parts) return ipv4_fast_fail so the general parser runs.
1360
ada_really_inline uint64_t try_parse_ipv4_avx512(const char* data,
1361
                                                 size_t len) noexcept {
1362
  // One trailing dot is WHATWG-legal ("1.2.3.4."); the SIMD kernel is
1363
  // strict four-group dotted-decimal.
1364
  if (data[len - 1] == '.') {
1365
    --len;
1366
  }
1367
  if (len > 15) [[unlikely]] {
1368
    return ipv4_fast_fail;
1369
  }
1370
1371
#if defined(__BMI2__)
1372
  const uint32_t len_mask = _bzhi_u32(0xFFFFFFFFu, static_cast<unsigned>(len));
1373
#else
1374
  const uint32_t len_mask = (1u << static_cast<unsigned>(len)) - 1u;
1375
#endif
1376
  const __mmask16 len_k = static_cast<__mmask16>(len_mask);
1377
  const __m128i dot = _mm_set1_epi8('.');
1378
  const __m128i v =
1379
      _mm_mask_loadu_epi8(dot, len_k, reinterpret_cast<const void*>(data));
1380
1381
  const __mmask16 delim = _mm_cmpeq_epi8_mask(v, dot);
1382
  const uint32_t dots = static_cast<uint32_t>(delim) & len_mask;
1383
  const uint32_t keep = len_mask & ~dots;
1384
1385
  const __m128i zero = _mm_set1_epi8('0');
1386
  const __m128i digits = _mm_sub_epi8(v, zero);
1387
  const __mmask16 is_digit = _mm_cmple_epu8_mask(digits, _mm_set1_epi8(9));
1388
  // Junk in a digit slot: inside [0, len) yet neither a digit nor a dot.
1389
  const __mmask16 hole = _kandn_mask16(_kor_mask16(delim, is_digit), len_k);
1390
  const __m128i v0 = _mm_maskz_mov_epi8(is_digit, digits);
1391
1392
  const __m128i iota =
1393
      _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
1394
  const __m128i c = _mm_maskz_compress_epi8(delim, iota);
1395
  const __m128i k_rep =
1396
      _mm_setr_epi8(0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3);
1397
  const __m128i qi = _mm_shuffle_epi8(c, k_rep);
1398
  const __m128i prev =
1399
      _mm_shuffle_epi8(_mm_alignr_epi8(c, _mm_set1_epi8(-1), 15), k_rep);
1400
  // Reversed group order: lane 4i+j fetches q_i-(j+1), so the dword is
1401
  // ones | tens<<8 | hundreds<<16, monotone in the decimal value.
1402
  const __m128i offr = _mm_setr_epi8(-1, -2, -3, -4, -1, -2, -3, -4, -1, -2, -3,
1403
                                     -4, -1, -2, -3, -4);
1404
  const __m128i idx = _mm_max_epi8(_mm_add_epi8(qi, offr), prev);
1405
  const __m128i padded = _mm_shuffle_epi8(v0, idx);
1406
1407
  const __m128i lim =
1408
      _mm_setr_epi8(5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0, 5, 5, 2, 0);
1409
  const __mmask8 over = _mm_cmpgt_epu32_mask(padded, lim);
1410
  const __m128i wtsr =
1411
      _mm_setr_epi8(1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0, 1, 10, 100, 0);
1412
#if defined(__AVX512VNNI__)
1413
  const __m128i res = _mm_dpbusd_epi32(_mm_setzero_si128(), padded, wtsr);
1414
#else
1415
  const __m128i res =
1416
      _mm_madd_epi16(_mm_maddubs_epi16(padded, wtsr), _mm_set1_epi16(1));
1417
#endif
1418
1419
  const __m128i gmin =
1420
      _mm_setr_epi8(1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1421
  const __m128i gmax =
1422
      _mm_setr_epi8(2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
1423
  const __m128i gap = _mm_sub_epi8(c, _mm_slli_si128(c, 1));
1424
  const __mmask16 bad_gap = _mm_cmpgt_epu8_mask(_mm_sub_epi8(gap, gmin), gmax);
1425
1426
  const uint32_t zero_bits =
1427
      static_cast<uint32_t>(_mm_cmpeq_epi8_mask(v, zero));
1428
  const uint32_t start_bits = (dots << 1) | 1u;
1429
1430
  const __mmask16 kerr =
1431
      _kor_mask16(_kor_mask16(hole, static_cast<__mmask16>(over)), bad_gap);
1432
  const uint32_t gerr = static_cast<uint32_t>(_mm_popcnt_u32(dots) ^ 3u) |
1433
                        (zero_bits & start_bits & (keep >> 1));
1434
1435
  if ((gerr | static_cast<uint32_t>(kerr)) == 0) [[likely]] {
1436
    const uint32_t packed =
1437
        static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_cvtepi32_epi8(res)));
1438
#if defined(__GNUC__) || defined(__clang__)
1439
    return static_cast<uint64_t>(__builtin_bswap32(packed));
1440
#else
1441
    return static_cast<uint64_t>(_byteswap_ulong(packed));
1442
#endif
1443
  }
1444
  return ipv4_fast_fail;
1445
}
1446
#endif  // ADA_AVX512 && __AVX512VBMI2__
1447
1448
}  // namespace detail
1449
1450
/**
1451
 * Fast pure-decimal IPv4 parse. Returns packed address or ipv4_fast_fail.
1452
 * Accepts an optional single trailing dot.
1453
 *
1454
 * On AVX-512BW+VL+VBMI2 targets, uses a table-free SIMD parse (masked
1455
 * load, no source over-read) based on parse_ipv4_avx512vl_notab5.
1456
 * Otherwise uses an unrolled scalar path (typically faster than SSE2/NEON
1457
 * pre-validation for these 7-16 byte hosts).
1458
 */
1459
ada_really_inline uint64_t
1460
294k
try_parse_ipv4_fast(std::string_view input) noexcept {
1461
294k
  const size_t len = input.size();
1462
  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
1463
294k
  if (len < 7 || len > 16) [[unlikely]] {
1464
214k
    return ipv4_fast_fail;
1465
214k
  }
1466
80.5k
  const char* data = input.data();
1467
1468
#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
1469
  return detail::try_parse_ipv4_avx512(data, len);
1470
#else
1471
80.5k
  return detail::parse_ipv4_decimal_scalar(data, data + len);
1472
294k
#endif
1473
294k
}
1474
1475
}  // namespace ada::checkers
1476
1477
#endif  // ADA_CHECKERS_INL_H
1478
/* end file include/ada/checkers-inl.h */
1479
/* begin file include/ada/log.h */
1480
/**
1481
 * @file log.h
1482
 * @brief Includes the definitions for logging.
1483
 * @private Excluded from docs through the doxygen file.
1484
 */
1485
#ifndef ADA_LOG_H
1486
#define ADA_LOG_H
1487
1488
// To enable logging, set ADA_LOGGING to 1:
1489
#ifndef ADA_LOGGING
1490
#define ADA_LOGGING 0
1491
#endif
1492
1493
#if ADA_LOGGING
1494
#include <iostream>
1495
#endif  // ADA_LOGGING
1496
1497
namespace ada {
1498
1499
/**
1500
 * Log a message. If you want to have no overhead when logging is disabled, use
1501
 * the ada_log macro.
1502
 * @private
1503
 */
1504
template <typename... Args>
1505
constexpr ada_really_inline void log([[maybe_unused]] Args... args) {
1506
#if ADA_LOGGING
1507
  ((std::cout << "ADA_LOG: ") << ... << args) << std::endl;
1508
#endif  // ADA_LOGGING
1509
}
1510
}  // namespace ada
1511
1512
#if ADA_LOGGING
1513
#ifndef ada_log
1514
#define ada_log(...)       \
1515
  do {                     \
1516
    ada::log(__VA_ARGS__); \
1517
  } while (0)
1518
#endif  // ada_log
1519
#else
1520
#define ada_log(...)
1521
#endif  // ADA_LOGGING
1522
1523
#endif  // ADA_LOG_H
1524
/* end file include/ada/log.h */
1525
/* begin file include/ada/encoding_type.h */
1526
/**
1527
 * @file encoding_type.h
1528
 * @brief Character encoding type definitions.
1529
 *
1530
 * Defines the encoding types supported for URL processing.
1531
 *
1532
 * @see https://encoding.spec.whatwg.org/
1533
 */
1534
#ifndef ADA_ENCODING_TYPE_H
1535
#define ADA_ENCODING_TYPE_H
1536
1537
#include <string>
1538
#include <string_view>
1539
1540
namespace ada {
1541
1542
/**
1543
 * @brief Character encoding types for URL processing.
1544
 *
1545
 * Specifies the character encoding used for percent-decoding and other
1546
 * string operations. UTF-8 is the most commonly used encoding for URLs.
1547
 *
1548
 * @see https://encoding.spec.whatwg.org/#encodings
1549
 */
1550
enum class encoding_type {
1551
  UTF8,     /**< UTF-8 encoding (default for URLs) */
1552
  UTF_16LE, /**< UTF-16 Little Endian encoding */
1553
  UTF_16BE, /**< UTF-16 Big Endian encoding */
1554
};
1555
1556
/**
1557
 * Converts an encoding_type to its string representation.
1558
 * @param type The encoding type to convert.
1559
 * @return A string view of the encoding name.
1560
 */
1561
ada_warn_unused std::string_view to_string(encoding_type type);
1562
1563
}  // namespace ada
1564
1565
#endif  // ADA_ENCODING_TYPE_H
1566
/* end file include/ada/encoding_type.h */
1567
/* begin file include/ada/helpers.h */
1568
/**
1569
 * @file helpers.h
1570
 * @brief Definitions for helper functions used within Ada.
1571
 */
1572
#ifndef ADA_HELPERS_H
1573
#define ADA_HELPERS_H
1574
1575
/* begin file include/ada/url_base.h */
1576
/**
1577
 * @file url_base.h
1578
 * @brief Base class and common definitions for URL types.
1579
 *
1580
 * This file defines the `url_base` abstract base class from which both
1581
 * `ada::url` and `ada::url_aggregator` inherit. It also defines common
1582
 * enumerations like `url_host_type`.
1583
 */
1584
#ifndef ADA_URL_BASE_H
1585
#define ADA_URL_BASE_H
1586
1587
/* begin file include/ada/scheme.h */
1588
/**
1589
 * @file scheme.h
1590
 * @brief URL scheme type definitions and utilities.
1591
 *
1592
 * This header defines the URL scheme types (http, https, etc.) and provides
1593
 * functions to identify special schemes and their default ports according
1594
 * to the WHATWG URL Standard.
1595
 *
1596
 * @see https://url.spec.whatwg.org/#special-scheme
1597
 */
1598
#ifndef ADA_SCHEME_H
1599
#define ADA_SCHEME_H
1600
1601
1602
#include <cstdint>
1603
#include <string>
1604
#include <string_view>
1605
1606
/**
1607
 * @namespace ada::scheme
1608
 * @brief URL scheme utilities and constants.
1609
 *
1610
 * Provides functions for working with URL schemes, including identification
1611
 * of special schemes and retrieval of default port numbers.
1612
 */
1613
namespace ada::scheme {
1614
1615
/**
1616
 * @brief Enumeration of URL scheme types.
1617
 *
1618
 * Special schemes have specific parsing rules and default ports.
1619
 * Using an enum allows efficient scheme comparisons without string operations.
1620
 *
1621
 * Default ports:
1622
 * - HTTP: 80
1623
 * - HTTPS: 443
1624
 * - WS: 80
1625
 * - WSS: 443
1626
 * - FTP: 21
1627
 * - FILE: (none)
1628
 */
1629
enum type : uint8_t {
1630
  HTTP = 0,        /**< http:// scheme (port 80) */
1631
  NOT_SPECIAL = 1, /**< Non-special scheme (no default port) */
1632
  HTTPS = 2,       /**< https:// scheme (port 443) */
1633
  WS = 3,          /**< ws:// WebSocket scheme (port 80) */
1634
  FTP = 4,         /**< ftp:// scheme (port 21) */
1635
  WSS = 5,         /**< wss:// secure WebSocket scheme (port 443) */
1636
  FILE = 6         /**< file:// scheme (no default port) */
1637
};
1638
1639
/**
1640
 * Checks if a scheme string is a special scheme.
1641
 * @param scheme The scheme string to check (e.g., "http", "https").
1642
 * @return `true` if the scheme is special, `false` otherwise.
1643
 * @see https://url.spec.whatwg.org/#special-scheme
1644
 */
1645
ada_really_inline constexpr bool is_special(std::string_view scheme);
1646
1647
/**
1648
 * Returns the default port for a special scheme string.
1649
 * @param scheme The scheme string (e.g., "http", "https").
1650
 * @return The default port number, or 0 if not a special scheme.
1651
 * @see https://url.spec.whatwg.org/#special-scheme
1652
 */
1653
constexpr uint16_t get_special_port(std::string_view scheme) noexcept;
1654
1655
/**
1656
 * Returns the default port for a scheme type.
1657
 * @param type The scheme type enum value.
1658
 * @return The default port number, or 0 if not applicable.
1659
 * @see https://url.spec.whatwg.org/#special-scheme
1660
 */
1661
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept;
1662
1663
/**
1664
 * Converts a scheme string to its type enum.
1665
 * @param scheme The scheme string to convert.
1666
 * @return The corresponding scheme type, or NOT_SPECIAL if not recognized.
1667
 */
1668
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept;
1669
1670
}  // namespace ada::scheme
1671
1672
#endif  // ADA_SCHEME_H
1673
/* end file include/ada/scheme.h */
1674
1675
#include <cstddef>
1676
#include <cstdint>
1677
#include <string>
1678
#include <string_view>
1679
1680
namespace ada {
1681
1682
/**
1683
 * @brief Enum representing the type of host in a URL.
1684
 *
1685
 * Used to distinguish between regular domain names, IPv4 addresses,
1686
 * and IPv6 addresses for proper parsing and serialization.
1687
 */
1688
enum url_host_type : uint8_t {
1689
  /** Regular domain name (e.g., "www.example.com") */
1690
  DEFAULT = 0,
1691
  /** IPv4 address (e.g., "127.0.0.1") */
1692
  IPV4 = 1,
1693
  /** IPv6 address (e.g., "[::1]" or "[2001:db8::1]") */
1694
  IPV6 = 2,
1695
};
1696
1697
/**
1698
 * @brief Abstract base class for URL representations.
1699
 *
1700
 * The `url_base` class provides the common interface and state shared by
1701
 * both `ada::url` and `ada::url_aggregator`. It contains basic URL attributes
1702
 * like validity status and scheme type, but delegates component storage and
1703
 * access to derived classes.
1704
 *
1705
 * @note This is an abstract class and cannot be instantiated directly.
1706
 *       Use `ada::url` or `ada::url_aggregator` instead.
1707
 *
1708
 * @see url
1709
 * @see url_aggregator
1710
 */
1711
struct url_base {
1712
2.61M
  virtual ~url_base() = default;
1713
1714
  /**
1715
   * Indicates whether the URL was successfully parsed.
1716
   * Set to `false` if parsing failed (e.g., invalid URL syntax).
1717
   */
1718
  bool is_valid{true};
1719
1720
  /**
1721
   * Indicates whether the URL has an opaque path (non-hierarchical).
1722
   * Opaque paths occur in non-special URLs like `mailto:` or `javascript:`.
1723
   */
1724
  bool has_opaque_path{false};
1725
1726
  /**
1727
   * The type of the URL's host (domain, IPv4, or IPv6).
1728
   */
1729
  url_host_type host_type = url_host_type::DEFAULT;
1730
1731
  /**
1732
   * @private
1733
   * Internal representation of the URL's scheme type.
1734
   */
1735
  ada::scheme::type type{ada::scheme::type::NOT_SPECIAL};
1736
1737
  /**
1738
   * Checks if the URL has a special scheme (http, https, ws, wss, ftp, file).
1739
   * Special schemes have specific parsing rules and default ports.
1740
   * @return `true` if the scheme is special, `false` otherwise.
1741
   */
1742
  [[nodiscard]] ada_really_inline constexpr bool is_special() const noexcept;
1743
1744
  /**
1745
   * Returns the URL's origin (scheme + host + port for special URLs).
1746
   * @return A newly allocated string containing the serialized origin.
1747
   * @see https://url.spec.whatwg.org/#concept-url-origin
1748
   */
1749
  [[nodiscard]] virtual std::string get_origin() const = 0;
1750
1751
  /**
1752
   * Validates whether the hostname is a valid domain according to RFC 1034.
1753
   * Checks that the domain and its labels have valid lengths.
1754
   * @return `true` if the domain is valid, `false` otherwise.
1755
   */
1756
  [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0;
1757
1758
  /**
1759
   * @private
1760
   * Returns the default port for special schemes (e.g., 443 for https).
1761
   * Returns 0 for file:// URLs or non-special schemes.
1762
   */
1763
  [[nodiscard]] inline uint16_t get_special_port() const noexcept;
1764
1765
  /**
1766
   * @private
1767
   * Returns the default port for the URL's scheme, or 0 if none.
1768
   */
1769
  [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept;
1770
1771
  /**
1772
   * @private
1773
   * Parses a port number from the input string.
1774
   * @param view The string containing the port to parse.
1775
   * @param check_trailing_content Whether to validate no trailing characters.
1776
   * @return Number of bytes consumed on success, 0 on failure.
1777
   */
1778
  virtual size_t parse_port(std::string_view view,
1779
                            bool check_trailing_content) = 0;
1780
1781
  /** @private */
1782
0
  virtual ada_really_inline size_t parse_port(std::string_view view) {
1783
0
    return this->parse_port(view, false);
1784
0
  }
1785
1786
  /**
1787
   * Returns a JSON string representation of this URL for debugging.
1788
   * @return A JSON-formatted string with URL information.
1789
   */
1790
  [[nodiscard]] virtual std::string to_string() const = 0;
1791
1792
  /** @private */
1793
  virtual inline void clear_pathname() = 0;
1794
1795
  /** @private */
1796
  virtual inline void clear_search() = 0;
1797
1798
  /** @private */
1799
  [[nodiscard]] virtual inline bool has_hash() const noexcept = 0;
1800
1801
  /** @private */
1802
  [[nodiscard]] virtual inline bool has_search() const noexcept = 0;
1803
1804
};  // url_base
1805
1806
}  // namespace ada
1807
1808
#endif
1809
/* end file include/ada/url_base.h */
1810
1811
#include <cstddef>
1812
#include <cstdint>
1813
#include <optional>
1814
#include <string>
1815
#include <string_view>
1816
#include <utility>
1817
1818
#if ADA_DEVELOPMENT_CHECKS
1819
#include <cstdlib>
1820
#include <iostream>
1821
#endif  // ADA_DEVELOPMENT_CHECKS
1822
1823
/**
1824
 * These functions are not part of our public API and may
1825
 * change at any time.
1826
 *
1827
 * @private
1828
 * @namespace ada::helpers
1829
 * @brief Includes the definitions for helper functions
1830
 */
1831
namespace ada::helpers {
1832
1833
/**
1834
 * @private
1835
 */
1836
template <typename out_iter>
1837
void encode_json(std::string_view view, out_iter out);
1838
1839
/**
1840
 * @private
1841
 * This function is used to prune a fragment from a url, and returning the
1842
 * removed string if input has fragment.
1843
 *
1844
 * @details prune_hash seeks the first '#' and returns everything after it
1845
 * as a string_view, and modifies (in place) the input so that it points at
1846
 * everything before the '#'. If no '#' is found, the input is left unchanged
1847
 * and std::nullopt is returned.
1848
 *
1849
 * @attention The function is non-allocating and it does not throw.
1850
 * @returns Note that the returned string_view might be empty!
1851
 */
1852
ada_really_inline std::optional<std::string_view> prune_hash(
1853
    std::string_view& input) noexcept;
1854
1855
/**
1856
 * @private
1857
 * Defined by the URL specification, shorten a URLs paths.
1858
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1859
 * @returns Returns true if path is shortened.
1860
 */
1861
ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type);
1862
1863
/**
1864
 * @private
1865
 * Defined by the URL specification, shorten a URLs paths.
1866
 * @see https://url.spec.whatwg.org/#shorten-a-urls-path
1867
 * @returns Returns true if path is shortened.
1868
 */
1869
ada_really_inline bool shorten_path(std::string_view& path,
1870
                                    ada::scheme::type type);
1871
1872
/**
1873
 * @private
1874
 *
1875
 * Parse the path from the provided input and append to the existing
1876
 * (possibly empty) path. The input cannot contain tabs and spaces: it
1877
 * is the user's responsibility to check.
1878
 *
1879
 * The input is expected to be UTF-8.
1880
 *
1881
 * @see https://url.spec.whatwg.org/
1882
 */
1883
ada_really_inline void parse_prepared_path(std::string_view input,
1884
                                           ada::scheme::type type,
1885
                                           std::string& path);
1886
1887
/**
1888
 * @private
1889
 * Remove and mutate all ASCII tab or newline characters from an input.
1890
 */
1891
ada_really_inline void remove_ascii_tab_or_newline(std::string& input);
1892
1893
/**
1894
 * @private
1895
 * Return the substring from input going from index pos to the end.
1896
 */
1897
ada_really_inline constexpr std::string_view substring(std::string_view input,
1898
                                                       size_t pos);
1899
1900
/**
1901
 * @private
1902
 * Returns true if the string_view points within the string.
1903
 */
1904
bool overlaps(std::string_view input1, const std::string& input2) noexcept;
1905
1906
/**
1907
 * @private
1908
 * Return the substring from input going from index pos1 to the pos2 (non
1909
 * included). The length of the substring is pos2 - pos1.
1910
 */
1911
ada_really_inline constexpr std::string_view substring(std::string_view input,
1912
                                                       size_t pos1,
1913
1.70M
                                                       size_t pos2) {
1914
#if ADA_DEVELOPMENT_CHECKS
1915
  if (pos2 < pos1) {
1916
    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
1917
              << std::endl;
1918
    abort();
1919
  }
1920
#endif
1921
1.70M
  return input.substr(pos1, pos2 - pos1);
1922
1.70M
}
1923
1924
/**
1925
 * @private
1926
 * Modify the string_view so that it has the new size pos, assuming that pos <=
1927
 * input.size(). This function cannot throw.
1928
 */
1929
ada_really_inline void resize(std::string_view& input, size_t pos) noexcept;
1930
1931
/**
1932
 * @private
1933
 * Returns a host's delimiter location depending on the state of the instance,
1934
 * and whether a colon was found outside brackets. Used by the host parser.
1935
 */
1936
ada_really_inline std::pair<size_t, bool> get_host_delimiter_location(
1937
    bool is_special, std::string_view& view) noexcept;
1938
1939
/**
1940
 * @private
1941
 * Removes leading and trailing C0 control and whitespace characters from
1942
 * string.
1943
 */
1944
void trim_c0_whitespace(std::string_view& input) noexcept;
1945
1946
/**
1947
 * @private
1948
 * @see
1949
 * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
1950
 */
1951
template <class url_type>
1952
ada_really_inline void strip_trailing_spaces_from_opaque_path(url_type& url);
1953
1954
/**
1955
 * @private
1956
 * Finds the delimiter of a view in authority state.
1957
 */
1958
ada_really_inline size_t
1959
find_authority_delimiter_special(std::string_view view) noexcept;
1960
1961
/**
1962
 * @private
1963
 * Finds the delimiter of a view in authority state.
1964
 */
1965
ada_really_inline size_t
1966
find_authority_delimiter(std::string_view view) noexcept;
1967
1968
/**
1969
 * @private
1970
 */
1971
template <typename T, typename... Args>
1972
32.3k
inline void inner_concat(std::string& buffer, T t) {
1973
32.3k
  buffer.append(t);
1974
32.3k
}
void ada::helpers::inner_concat<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1972
6.62k
inline void inner_concat(std::string& buffer, T t) {
1973
6.62k
  buffer.append(t);
1974
6.62k
}
void ada::helpers::inner_concat<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*)
Line
Count
Source
1972
25.7k
inline void inner_concat(std::string& buffer, T t) {
1973
25.7k
  buffer.append(t);
1974
25.7k
}
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> >)
1975
1976
/**
1977
 * @private
1978
 */
1979
template <typename T, typename... Args>
1980
32.3k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
32.3k
  buffer.append(t);
1982
32.3k
  return inner_concat(buffer, args...);
1983
32.3k
}
void ada::helpers::inner_concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1980
6.62k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
6.62k
  buffer.append(t);
1982
6.62k
  return inner_concat(buffer, args...);
1983
6.62k
}
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> >)
void ada::helpers::inner_concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1980
25.7k
inline void inner_concat(std::string& buffer, T t, Args... args) {
1981
25.7k
  buffer.append(t);
1982
25.7k
  return inner_concat(buffer, args...);
1983
25.7k
}
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> >)
1984
1985
/**
1986
 * @private
1987
 * Concatenate the arguments and return a string.
1988
 * @returns a string
1989
 */
1990
template <typename... Args>
1991
32.3k
std::string concat(Args... args) {
1992
32.3k
  std::string answer;
1993
32.3k
  inner_concat(answer, args...);
1994
32.3k
  return answer;
1995
32.3k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
1991
6.62k
std::string concat(Args... args) {
1992
6.62k
  std::string answer;
1993
6.62k
  inner_concat(answer, args...);
1994
6.62k
  return answer;
1995
6.62k
}
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> >)
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > ada::helpers::concat<std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, char const*)
Line
Count
Source
1991
25.7k
std::string concat(Args... args) {
1992
25.7k
  std::string answer;
1993
25.7k
  inner_concat(answer, args...);
1994
25.7k
  return answer;
1995
25.7k
}
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> >)
1996
1997
/**
1998
 * @private
1999
 * @return Number of leading zeroes.
2000
 */
2001
789k
inline int leading_zeroes(uint32_t input_num) noexcept {
2002
#if ADA_REGULAR_VISUAL_STUDIO
2003
  unsigned long leading_zero(0);
2004
  unsigned long in(input_num);
2005
  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
2006
#else
2007
789k
  return __builtin_clz(input_num);
2008
789k
#endif  // ADA_REGULAR_VISUAL_STUDIO
2009
789k
}
2010
2011
/**
2012
 * @private
2013
 * Counts the number of decimal digits necessary to represent x.
2014
 * faster than std::to_string(x).size().
2015
 * @return digit count
2016
 */
2017
0
inline int fast_digit_count(uint32_t x) noexcept {
2018
0
  auto int_log2 = [](uint32_t z) -> int {
2019
0
    return 31 - ada::helpers::leading_zeroes(z | 1);
2020
0
  };
2021
0
  // Compiles to very few instructions. Note that the
2022
0
  // table is static and thus effectively a constant.
2023
0
  // We leave it inside the function because it is meaningless
2024
0
  // outside of it (this comes at no performance cost).
2025
0
  const static uint64_t table[] = {
2026
0
      4294967296,  8589934582,  8589934582,  8589934582,  12884901788,
2027
0
      12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
2028
0
      21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
2029
0
      25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
2030
0
      34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
2031
0
      38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
2032
0
      42949672960, 42949672960};
2033
0
  return int((x + table[int_log2(x)]) >> 32);
2034
0
}
2035
}  // namespace ada::helpers
2036
2037
#endif  // ADA_HELPERS_H
2038
/* end file include/ada/helpers.h */
2039
/* begin file include/ada/parser.h */
2040
/**
2041
 * @file parser.h
2042
 * @brief Low-level URL parsing functions.
2043
 *
2044
 * This header provides the internal URL parsing implementation. Most users
2045
 * should use `ada::parse()` from implementation.h instead of these functions
2046
 * directly.
2047
 *
2048
 * @see implementation.h for the recommended public API
2049
 */
2050
#ifndef ADA_PARSER_H
2051
#define ADA_PARSER_H
2052
2053
#include <cstddef>
2054
#include <cstdint>
2055
#include <string_view>
2056
#include <variant>
2057
2058
/* begin file include/ada/expected.h */
2059
/**
2060
 * @file expected.h
2061
 * @brief Definitions for std::expected
2062
 * @private Excluded from docs through the doxygen file.
2063
 */
2064
///
2065
// expected - An implementation of std::expected with extensions
2066
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
2067
//
2068
// Documentation available at http://tl.tartanllama.xyz/
2069
//
2070
// To the extent possible under law, the author(s) have dedicated all
2071
// copyright and related and neighboring rights to this software to the
2072
// public domain worldwide. This software is distributed without any warranty.
2073
//
2074
// You should have received a copy of the CC0 Public Domain Dedication
2075
// along with this software. If not, see
2076
// <http://creativecommons.org/publicdomain/zero/1.0/>.
2077
///
2078
2079
#ifndef TL_EXPECTED_HPP
2080
#define TL_EXPECTED_HPP
2081
2082
#define TL_EXPECTED_VERSION_MAJOR 1
2083
#define TL_EXPECTED_VERSION_MINOR 1
2084
#define TL_EXPECTED_VERSION_PATCH 0
2085
2086
#include <cstddef>
2087
#include <exception>
2088
#include <functional>
2089
#include <initializer_list>
2090
#include <type_traits>
2091
#include <utility>
2092
2093
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
2094
#define TL_EXPECTED_EXCEPTIONS_ENABLED
2095
#endif
2096
2097
#if (defined(_MSC_VER) && _MSC_VER == 1900)
2098
#define TL_EXPECTED_MSVC2015
2099
#define TL_EXPECTED_MSVC2015_CONSTEXPR
2100
#else
2101
#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
2102
#endif
2103
2104
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2105
     !defined(__clang__))
2106
#define TL_EXPECTED_GCC49
2107
#endif
2108
2109
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
2110
     !defined(__clang__))
2111
#define TL_EXPECTED_GCC54
2112
#endif
2113
2114
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
2115
     !defined(__clang__))
2116
#define TL_EXPECTED_GCC55
2117
#endif
2118
2119
#if !defined(TL_ASSERT)
2120
// can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
2121
#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
2122
#include <cassert>
2123
20.4M
#define TL_ASSERT(x) assert(x)
2124
#else
2125
#define TL_ASSERT(x)
2126
#endif
2127
#endif
2128
2129
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
2130
     !defined(__clang__))
2131
// GCC < 5 doesn't support overloading on const&& for member functions
2132
2133
#define TL_EXPECTED_NO_CONSTRR
2134
// GCC < 5 doesn't support some standard C++11 type traits
2135
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2136
  std::has_trivial_copy_constructor<T>
2137
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2138
  std::has_trivial_copy_assign<T>
2139
2140
// This one will be different for GCC 5.7 if it's ever supported
2141
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2142
  std::is_trivially_destructible<T>
2143
2144
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
2145
// std::vector for non-copyable types
2146
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
2147
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2148
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
2149
namespace tl {
2150
namespace detail {
2151
template <class T>
2152
struct is_trivially_copy_constructible
2153
    : std::is_trivially_copy_constructible<T> {};
2154
#ifdef _GLIBCXX_VECTOR
2155
template <class T, class A>
2156
struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
2157
#endif
2158
}  // namespace detail
2159
}  // namespace tl
2160
#endif
2161
2162
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2163
  tl::detail::is_trivially_copy_constructible<T>
2164
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2165
  std::is_trivially_copy_assignable<T>
2166
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2167
  std::is_trivially_destructible<T>
2168
#else
2169
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
2170
  std::is_trivially_copy_constructible<T>
2171
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
2172
  std::is_trivially_copy_assignable<T>
2173
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
2174
  std::is_trivially_destructible<T>
2175
#endif
2176
2177
#if __cplusplus > 201103L
2178
#define TL_EXPECTED_CXX14
2179
#endif
2180
2181
#ifdef TL_EXPECTED_GCC49
2182
#define TL_EXPECTED_GCC49_CONSTEXPR
2183
#else
2184
#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
2185
#endif
2186
2187
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
2188
     defined(TL_EXPECTED_GCC49))
2189
#define TL_EXPECTED_11_CONSTEXPR
2190
#else
2191
#define TL_EXPECTED_11_CONSTEXPR constexpr
2192
#endif
2193
2194
namespace tl {
2195
template <class T, class E>
2196
class expected;
2197
2198
#ifndef TL_MONOSTATE_INPLACE_MUTEX
2199
#define TL_MONOSTATE_INPLACE_MUTEX
2200
class monostate {};
2201
2202
struct in_place_t {
2203
  explicit in_place_t() = default;
2204
};
2205
static constexpr in_place_t in_place{};
2206
#endif
2207
2208
template <class E>
2209
class unexpected {
2210
 public:
2211
  static_assert(!std::is_same<E, void>::value, "E must not be void");
2212
2213
  unexpected() = delete;
2214
1.38k
  constexpr explicit unexpected(const E& e) : m_val(e) {}
2215
2216
2.80M
  constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
2217
2218
  template <class... Args, typename std::enable_if<std::is_constructible<
2219
                               E, Args&&...>::value>::type* = nullptr>
2220
  constexpr explicit unexpected(Args&&... args)
2221
81.0k
      : m_val(std::forward<Args>(args)...) {}
2222
  template <
2223
      class U, class... Args,
2224
      typename std::enable_if<std::is_constructible<
2225
          E, std::initializer_list<U>&, Args&&...>::value>::type* = nullptr>
2226
  constexpr explicit unexpected(std::initializer_list<U> l, Args&&... args)
2227
      : m_val(l, std::forward<Args>(args)...) {}
2228
2229
  constexpr const E& value() const& { return m_val; }
2230
1.52M
  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
2231
  TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
2232
  constexpr const E&& value() const&& { return std::move(m_val); }
2233
2234
 private:
2235
  E m_val;
2236
};
2237
2238
#ifdef __cpp_deduction_guides
2239
template <class E>
2240
unexpected(E) -> unexpected<E>;
2241
#endif
2242
2243
template <class E>
2244
constexpr bool operator==(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2245
  return lhs.value() == rhs.value();
2246
}
2247
template <class E>
2248
constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2249
  return lhs.value() != rhs.value();
2250
}
2251
template <class E>
2252
constexpr bool operator<(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2253
  return lhs.value() < rhs.value();
2254
}
2255
template <class E>
2256
constexpr bool operator<=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2257
  return lhs.value() <= rhs.value();
2258
}
2259
template <class E>
2260
constexpr bool operator>(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2261
  return lhs.value() > rhs.value();
2262
}
2263
template <class E>
2264
constexpr bool operator>=(const unexpected<E>& lhs, const unexpected<E>& rhs) {
2265
  return lhs.value() >= rhs.value();
2266
}
2267
2268
template <class E>
2269
unexpected<typename std::decay<E>::type> make_unexpected(E&& e) {
2270
  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
2271
}
2272
2273
struct unexpect_t {
2274
  unexpect_t() = default;
2275
};
2276
static constexpr unexpect_t unexpect{};
2277
2278
namespace detail {
2279
template <typename E>
2280
0
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e) {
2281
0
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2282
0
  throw std::forward<E>(e);
2283
#else
2284
  (void)e;
2285
#ifdef _MSC_VER
2286
  __assume(0);
2287
#else
2288
  __builtin_unreachable();
2289
#endif
2290
#endif
2291
0
}
2292
2293
#ifndef TL_TRAITS_MUTEX
2294
#define TL_TRAITS_MUTEX
2295
// C++14-style aliases for brevity
2296
template <class T>
2297
using remove_const_t = typename std::remove_const<T>::type;
2298
template <class T>
2299
using remove_reference_t = typename std::remove_reference<T>::type;
2300
template <class T>
2301
using decay_t = typename std::decay<T>::type;
2302
template <bool E, class T = void>
2303
using enable_if_t = typename std::enable_if<E, T>::type;
2304
template <bool B, class T, class F>
2305
using conditional_t = typename std::conditional<B, T, F>::type;
2306
2307
// std::conjunction from C++17
2308
template <class...>
2309
struct conjunction : std::true_type {};
2310
template <class B>
2311
struct conjunction<B> : B {};
2312
template <class B, class... Bs>
2313
struct conjunction<B, Bs...>
2314
    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
2315
2316
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
2317
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2318
#endif
2319
2320
// In C++11 mode, there's an issue in libc++'s std::mem_fn
2321
// which results in a hard-error when using it in a noexcept expression
2322
// in some cases. This is a check to workaround the common failing case.
2323
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2324
template <class T>
2325
struct is_pointer_to_non_const_member_func : std::false_type {};
2326
template <class T, class Ret, class... Args>
2327
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
2328
    : std::true_type {};
2329
template <class T, class Ret, class... Args>
2330
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
2331
    : std::true_type {};
2332
template <class T, class Ret, class... Args>
2333
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
2334
    : std::true_type {};
2335
template <class T, class Ret, class... Args>
2336
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
2337
    : std::true_type {};
2338
template <class T, class Ret, class... Args>
2339
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
2340
    : std::true_type {};
2341
template <class T, class Ret, class... Args>
2342
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
2343
    : std::true_type {};
2344
2345
template <class T>
2346
struct is_const_or_const_ref : std::false_type {};
2347
template <class T>
2348
struct is_const_or_const_ref<T const&> : std::true_type {};
2349
template <class T>
2350
struct is_const_or_const_ref<T const> : std::true_type {};
2351
#endif
2352
2353
// std::invoke from C++17
2354
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
2355
template <
2356
    typename Fn, typename... Args,
2357
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
2358
    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
2359
                             is_const_or_const_ref<Args...>::value)>,
2360
#endif
2361
    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
2362
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2363
    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
2364
    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
2365
  return std::mem_fn(f)(std::forward<Args>(args)...);
2366
}
2367
2368
template <typename Fn, typename... Args,
2369
          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
2370
constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
2371
    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
2372
    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
2373
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
2374
}
2375
2376
// std::invoke_result from C++17
2377
template <class F, class, class... Us>
2378
struct invoke_result_impl;
2379
2380
template <class F, class... Us>
2381
struct invoke_result_impl<
2382
    F,
2383
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
2384
    Us...> {
2385
  using type =
2386
      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
2387
};
2388
2389
template <class F, class... Us>
2390
using invoke_result = invoke_result_impl<F, void, Us...>;
2391
2392
template <class F, class... Us>
2393
using invoke_result_t = typename invoke_result<F, Us...>::type;
2394
2395
#if defined(_MSC_VER) && _MSC_VER <= 1900
2396
// TODO make a version which works with MSVC 2015
2397
template <class T, class U = T>
2398
struct is_swappable : std::true_type {};
2399
2400
template <class T, class U = T>
2401
struct is_nothrow_swappable : std::true_type {};
2402
#else
2403
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
2404
namespace swap_adl_tests {
2405
// if swap ADL finds this then it would call std::swap otherwise (same
2406
// signature)
2407
struct tag {};
2408
2409
template <class T>
2410
tag swap(T&, T&);
2411
template <class T, std::size_t N>
2412
tag swap(T (&a)[N], T (&b)[N]);
2413
2414
// helper functions to test if an unqualified swap is possible, and if it
2415
// becomes std::swap
2416
template <class, class>
2417
std::false_type can_swap(...) noexcept(false);
2418
template <class T, class U,
2419
          class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
2420
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
2421
                                                    std::declval<U&>())));
2422
2423
template <class, class>
2424
std::false_type uses_std(...);
2425
template <class T, class U>
2426
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
2427
uses_std(int);
2428
2429
template <class T>
2430
struct is_std_swap_noexcept
2431
    : std::integral_constant<bool,
2432
                             std::is_nothrow_move_constructible<T>::value &&
2433
                                 std::is_nothrow_move_assignable<T>::value> {};
2434
2435
template <class T, std::size_t N>
2436
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
2437
2438
template <class T, class U>
2439
struct is_adl_swap_noexcept
2440
    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
2441
}  // namespace swap_adl_tests
2442
2443
template <class T, class U = T>
2444
struct is_swappable
2445
    : std::integral_constant<
2446
          bool,
2447
          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
2448
              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
2449
               (std::is_move_assignable<T>::value &&
2450
                std::is_move_constructible<T>::value))> {};
2451
2452
template <class T, std::size_t N>
2453
struct is_swappable<T[N], T[N]>
2454
    : std::integral_constant<
2455
          bool,
2456
          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
2457
              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
2458
                   0))::value ||
2459
               is_swappable<T, T>::value)> {};
2460
2461
template <class T, class U = T>
2462
struct is_nothrow_swappable
2463
    : std::integral_constant<
2464
          bool,
2465
          is_swappable<T, U>::value &&
2466
              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2467
                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
2468
               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
2469
                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
2470
#endif
2471
#endif
2472
2473
// Trait for checking if a type is a tl::expected
2474
template <class T>
2475
struct is_expected_impl : std::false_type {};
2476
template <class T, class E>
2477
struct is_expected_impl<expected<T, E>> : std::true_type {};
2478
template <class T>
2479
using is_expected = is_expected_impl<decay_t<T>>;
2480
2481
template <class T, class E, class U>
2482
using expected_enable_forward_value = detail::enable_if_t<
2483
    std::is_constructible<T, U&&>::value &&
2484
    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
2485
    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
2486
    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
2487
2488
template <class T, class E, class U, class G, class UR, class GR>
2489
using expected_enable_from_other = detail::enable_if_t<
2490
    std::is_constructible<T, UR>::value &&
2491
    std::is_constructible<E, GR>::value &&
2492
    !std::is_constructible<T, expected<U, G>&>::value &&
2493
    !std::is_constructible<T, expected<U, G>&&>::value &&
2494
    !std::is_constructible<T, const expected<U, G>&>::value &&
2495
    !std::is_constructible<T, const expected<U, G>&&>::value &&
2496
    !std::is_convertible<expected<U, G>&, T>::value &&
2497
    !std::is_convertible<expected<U, G>&&, T>::value &&
2498
    !std::is_convertible<const expected<U, G>&, T>::value &&
2499
    !std::is_convertible<const expected<U, G>&&, T>::value>;
2500
2501
template <class T, class U>
2502
using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
2503
2504
template <class T>
2505
using is_copy_constructible_or_void =
2506
    is_void_or<T, std::is_copy_constructible<T>>;
2507
2508
template <class T>
2509
using is_move_constructible_or_void =
2510
    is_void_or<T, std::is_move_constructible<T>>;
2511
2512
template <class T>
2513
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
2514
2515
template <class T>
2516
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
2517
2518
}  // namespace detail
2519
2520
namespace detail {
2521
struct no_init_t {};
2522
static constexpr no_init_t no_init{};
2523
2524
// Implements the storage of the values, and ensures that the destructor is
2525
// trivial if it can be.
2526
//
2527
// This specialization is for where neither `T` or `E` is trivially
2528
// destructible, so the destructors must be called on destruction of the
2529
// `expected`
2530
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
2531
          bool = std::is_trivially_destructible<E>::value>
2532
struct expected_storage_base {
2533
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2534
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2535
2536
  template <class... Args,
2537
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2538
                nullptr>
2539
  constexpr expected_storage_base(in_place_t, Args&&... args)
2540
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2541
2542
  template <class U, class... Args,
2543
            detail::enable_if_t<std::is_constructible<
2544
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2545
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2546
                                  Args&&... args)
2547
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2548
  template <class... Args,
2549
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2550
                nullptr>
2551
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2552
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2553
2554
  template <class U, class... Args,
2555
            detail::enable_if_t<std::is_constructible<
2556
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2557
  constexpr explicit expected_storage_base(unexpect_t,
2558
                                           std::initializer_list<U> il,
2559
                                           Args&&... args)
2560
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2561
2562
  ~expected_storage_base() {
2563
    if (m_has_val) {
2564
      m_val.~T();
2565
    } else {
2566
      m_unexpect.~unexpected<E>();
2567
    }
2568
  }
2569
  union {
2570
    T m_val;
2571
    unexpected<E> m_unexpect;
2572
    char m_no_init;
2573
  };
2574
  bool m_has_val;
2575
};
2576
2577
// This specialization is for when both `T` and `E` are trivially-destructible,
2578
// so the destructor of the `expected` can be trivial.
2579
template <class T, class E>
2580
struct expected_storage_base<T, E, true, true> {
2581
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2582
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2583
2584
  template <class... Args,
2585
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2586
                nullptr>
2587
  constexpr expected_storage_base(in_place_t, Args&&... args)
2588
388k
      : 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_
2589
2590
  template <class U, class... Args,
2591
            detail::enable_if_t<std::is_constructible<
2592
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2593
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2594
                                  Args&&... args)
2595
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2596
  template <class... Args,
2597
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2598
                nullptr>
2599
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2600
0
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2601
2602
  template <class U, class... Args,
2603
            detail::enable_if_t<std::is_constructible<
2604
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2605
  constexpr explicit expected_storage_base(unexpect_t,
2606
                                           std::initializer_list<U> il,
2607
                                           Args&&... args)
2608
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2609
2610
  ~expected_storage_base() = default;
2611
  union {
2612
    T m_val;
2613
    unexpected<E> m_unexpect;
2614
    char m_no_init;
2615
  };
2616
  bool m_has_val;
2617
};
2618
2619
// T is trivial, E is not.
2620
template <class T, class E>
2621
struct expected_storage_base<T, E, true, false> {
2622
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2623
  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
2624
      : m_no_init(), m_has_val(false) {}
2625
2626
  template <class... Args,
2627
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2628
                nullptr>
2629
  constexpr expected_storage_base(in_place_t, Args&&... args)
2630
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2631
2632
  template <class U, class... Args,
2633
            detail::enable_if_t<std::is_constructible<
2634
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2635
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2636
                                  Args&&... args)
2637
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2638
  template <class... Args,
2639
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2640
                nullptr>
2641
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2642
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2643
2644
  template <class U, class... Args,
2645
            detail::enable_if_t<std::is_constructible<
2646
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2647
  constexpr explicit expected_storage_base(unexpect_t,
2648
                                           std::initializer_list<U> il,
2649
                                           Args&&... args)
2650
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2651
2652
  ~expected_storage_base() {
2653
    if (!m_has_val) {
2654
      m_unexpect.~unexpected<E>();
2655
    }
2656
  }
2657
2658
  union {
2659
    T m_val;
2660
    unexpected<E> m_unexpect;
2661
    char m_no_init;
2662
  };
2663
  bool m_has_val;
2664
};
2665
2666
// E is trivial, T is not.
2667
template <class T, class E>
2668
struct expected_storage_base<T, E, false, true> {
2669
  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
2670
0
  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
2671
2672
  template <class... Args,
2673
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
2674
                nullptr>
2675
  constexpr expected_storage_base(in_place_t, Args&&... args)
2676
7.54M
      : 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_
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2676
844k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
2676
395k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2676
1.02M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA1_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Line
Count
Source
2676
1.13M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJRA2_KcETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESJ_
Line
Count
Source
2676
49
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJRS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESH_
Line
Count
Source
2676
1.19M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
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_
_ZN2tl6detail21expected_storage_baseINSt3__18optionalIN3ada18url_pattern_resultEEENS4_6errorsELb0ELb1EEC2IJRKNS2_9nullopt_tEETnPNS2_9enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2676
462k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__18optionalIN3ada18url_pattern_resultEEENS4_6errorsELb0ELb1EEC2IJS5_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
2676
55.6k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJRS8_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
2676
1.10M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJRS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
Line
Count
Source
2676
88.6k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada21url_pattern_componentINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS6_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESE_
Line
Count
Source
2676
1.10M
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN2tl6detail21expected_storage_baseIN3ada11url_patternINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS6_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESE_
Line
Count
Source
2676
129k
      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
2677
2678
  template <class U, class... Args,
2679
            detail::enable_if_t<std::is_constructible<
2680
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2681
  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
2682
                                  Args&&... args)
2683
      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
2684
  template <class... Args,
2685
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2686
                nullptr>
2687
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2688
1.44M
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2688
1.25M
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada16url_pattern_initENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_
Line
Count
Source
2688
59.6k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEN3ada6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Line
Count
Source
2688
5.04k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS2_9allocatorIS6_EEEENS4_6errorsELb0ELb1EEC2IJSA_ETnPNS2_9enable_ifIXsr3std16is_constructibleISA_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESG_
Line
Count
Source
2688
1.50k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseIN3ada23url_pattern_list_detail15compiled_routesENS2_6errorsELb0ELb1EEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESC_
Unexecuted instantiation: _ZN2tl6detail21expected_storage_baseINSt3__18optionalIN3ada18url_pattern_resultEEENS4_6errorsELb0ELb1EEC2IJS7_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESD_
_ZN2tl6detail21expected_storage_baseINSt3__16vectorIN3ada16url_pattern_partENS2_9allocatorIS5_EEEENS4_6errorsELb0ELb1EEC2IJS9_ETnPNS2_9enable_ifIXsr3std16is_constructibleIS9_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESF_
Line
Count
Source
2688
7.11k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada11url_patternINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS7_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESE_
Line
Count
Source
2688
110k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail21expected_storage_baseIN3ada21url_pattern_componentINS2_17url_pattern_regex18std_regex_providerEEENS2_6errorsELb0ELb1EEC2IJS7_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESE_
Line
Count
Source
2688
8.28k
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2689
2690
  template <class U, class... Args,
2691
            detail::enable_if_t<std::is_constructible<
2692
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2693
  constexpr explicit expected_storage_base(unexpect_t,
2694
                                           std::initializer_list<U> il,
2695
                                           Args&&... args)
2696
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2697
2698
8.98M
  ~expected_storage_base() {
2699
8.98M
    if (m_has_val) {
2700
7.80M
      m_val.~T();
2701
7.80M
    }
2702
8.98M
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url, ada::errors, false, true>::~expected_storage_base()
tl::detail::expected_storage_base<ada::url_aggregator, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
2.09M
  ~expected_storage_base() {
2699
2.09M
    if (m_has_val) {
2700
1.10M
      m_val.~T();
2701
1.10M
    }
2702
2.09M
  }
tl::detail::expected_storage_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
2.17M
  ~expected_storage_base() {
2699
2.17M
    if (m_has_val) {
2700
2.16M
      m_val.~T();
2701
2.16M
    }
2702
2.17M
  }
tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
1.11M
  ~expected_storage_base() {
2699
1.11M
    if (m_has_val) {
2700
1.10M
      m_val.~T();
2701
1.10M
    }
2702
1.11M
  }
Unexecuted instantiation: tl::detail::expected_storage_base<ada::url_search_params, ada::errors, false, true>::~expected_storage_base()
Unexecuted instantiation: tl::detail::expected_storage_base<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors, false, true>::~expected_storage_base()
tl::detail::expected_storage_base<ada::url_pattern_init, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
543k
  ~expected_storage_base() {
2699
543k
    if (m_has_val) {
2700
483k
      m_val.~T();
2701
483k
    }
2702
543k
  }
tl::detail::expected_storage_base<std::__1::optional<ada::url_pattern_result>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
518k
  ~expected_storage_base() {
2699
518k
    if (m_has_val) {
2700
518k
      m_val.~T();
2701
518k
    }
2702
518k
  }
tl::detail::expected_storage_base<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
240k
  ~expected_storage_base() {
2699
240k
    if (m_has_val) {
2700
129k
      m_val.~T();
2701
129k
    }
2702
240k
  }
tl::detail::expected_storage_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
1.20M
  ~expected_storage_base() {
2699
1.20M
    if (m_has_val) {
2700
1.19M
      m_val.~T();
2701
1.19M
    }
2702
1.20M
  }
tl::detail::expected_storage_base<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors, false, true>::~expected_storage_base()
Line
Count
Source
2698
1.11M
  ~expected_storage_base() {
2699
1.11M
    if (m_has_val) {
2700
1.10M
      m_val.~T();
2701
1.10M
    }
2702
1.11M
  }
2703
  union {
2704
    T m_val;
2705
    unexpected<E> m_unexpect;
2706
    char m_no_init;
2707
  };
2708
  bool m_has_val;
2709
};
2710
2711
// `T` is `void`, `E` is trivially-destructible
2712
template <class E>
2713
struct expected_storage_base<void, E, false, true> {
2714
#if __GNUC__ <= 5
2715
// no constexpr for GCC 4/5 bug
2716
#else
2717
  TL_EXPECTED_MSVC2015_CONSTEXPR
2718
#endif
2719
  expected_storage_base() : m_has_val(true) {}
2720
2721
  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
2722
2723
  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
2724
2725
  template <class... Args,
2726
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2727
                nullptr>
2728
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2729
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2730
2731
  template <class U, class... Args,
2732
            detail::enable_if_t<std::is_constructible<
2733
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2734
  constexpr explicit expected_storage_base(unexpect_t,
2735
                                           std::initializer_list<U> il,
2736
                                           Args&&... args)
2737
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2738
2739
  ~expected_storage_base() = default;
2740
  struct dummy {};
2741
  union {
2742
    unexpected<E> m_unexpect;
2743
    dummy m_val;
2744
  };
2745
  bool m_has_val;
2746
};
2747
2748
// `T` is `void`, `E` is not trivially-destructible
2749
template <class E>
2750
struct expected_storage_base<void, E, false, false> {
2751
  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
2752
  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
2753
2754
  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
2755
2756
  template <class... Args,
2757
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
2758
                nullptr>
2759
  constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
2760
      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
2761
2762
  template <class U, class... Args,
2763
            detail::enable_if_t<std::is_constructible<
2764
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
2765
  constexpr explicit expected_storage_base(unexpect_t,
2766
                                           std::initializer_list<U> il,
2767
                                           Args&&... args)
2768
      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
2769
2770
  ~expected_storage_base() {
2771
    if (!m_has_val) {
2772
      m_unexpect.~unexpected<E>();
2773
    }
2774
  }
2775
2776
  union {
2777
    unexpected<E> m_unexpect;
2778
    char m_dummy;
2779
  };
2780
  bool m_has_val;
2781
};
2782
2783
// This base class provides some handy member functions which can be used in
2784
// further derived classes
2785
template <class T, class E>
2786
struct expected_operations_base : expected_storage_base<T, E> {
2787
  using expected_storage_base<T, E>::expected_storage_base;
2788
2789
  template <class... Args>
2790
259k
  void construct(Args&&... args) noexcept {
2791
259k
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
2792
259k
    this->m_has_val = true;
2793
259k
  }
2794
2795
  template <class Rhs>
2796
  // NOLINTNEXTLINE(bugprone-exception-escape)
2797
0
  void construct_with(Rhs&& rhs) noexcept {
2798
0
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
2799
0
    this->m_has_val = true;
2800
0
  }
2801
2802
  template <class... Args>
2803
0
  void construct_error(Args&&... args) noexcept {
2804
0
    new (std::addressof(this->m_unexpect))
2805
0
        unexpected<E>(std::forward<Args>(args)...);
2806
0
    this->m_has_val = false;
2807
0
  }
Unexecuted instantiation: void tl::detail::expected_operations_base<ada::url_aggregator, ada::errors>::construct_error<tl::unexpected<ada::errors> const&>(tl::unexpected<ada::errors> const&)
Unexecuted instantiation: void tl::detail::expected_operations_base<ada::url_aggregator, ada::errors>::construct_error<tl::unexpected<ada::errors> >(tl::unexpected<ada::errors>&&)
2808
2809
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2810
2811
  // These assign overloads ensure that the most efficient assignment
2812
  // implementation is used while maintaining the strong exception guarantee.
2813
  // The problematic case is where rhs has a value, but *this does not.
2814
  //
2815
  // This overload handles the case where we can just copy-construct `T`
2816
  // directly into place without throwing.
2817
  template <class U = T,
2818
            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* =
2819
                nullptr>
2820
  void assign(const expected_operations_base& rhs) noexcept {
2821
    if (!this->m_has_val && rhs.m_has_val) {
2822
      geterr().~unexpected<E>();
2823
      construct(rhs.get());
2824
    } else {
2825
      assign_common(rhs);
2826
    }
2827
  }
2828
2829
  // This overload handles the case where we can attempt to create a copy of
2830
  // `T`, then no-throw move it into place if the copy was successful.
2831
  template <class U = T,
2832
            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
2833
                                std::is_nothrow_move_constructible<U>::value>* =
2834
                nullptr>
2835
  void assign(const expected_operations_base& rhs) noexcept {
2836
    if (!this->m_has_val && rhs.m_has_val) {
2837
      T tmp = rhs.get();
2838
      geterr().~unexpected<E>();
2839
      construct(std::move(tmp));
2840
    } else {
2841
      assign_common(rhs);
2842
    }
2843
  }
2844
2845
  // This overload is the worst-case, where we have to move-construct the
2846
  // unexpected value into temporary storage, then try to copy the T into place.
2847
  // If the construction succeeds, then everything is fine, but if it throws,
2848
  // then we move the old unexpected value back into place before rethrowing the
2849
  // exception.
2850
  template <class U = T,
2851
            detail::enable_if_t<
2852
                !std::is_nothrow_copy_constructible<U>::value &&
2853
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2854
  void assign(const expected_operations_base& rhs) {
2855
    if (!this->m_has_val && rhs.m_has_val) {
2856
      auto tmp = std::move(geterr());
2857
      geterr().~unexpected<E>();
2858
2859
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2860
      try {
2861
        construct(rhs.get());
2862
      } catch (...) {
2863
        geterr() = std::move(tmp);
2864
        throw;
2865
      }
2866
#else
2867
      construct(rhs.get());
2868
#endif
2869
    } else {
2870
      assign_common(rhs);
2871
    }
2872
  }
2873
2874
  // These overloads do the same as above, but for rvalues
2875
  template <class U = T,
2876
            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* =
2877
                nullptr>
2878
259k
  void assign(expected_operations_base&& rhs) noexcept {
2879
259k
    if (!this->m_has_val && rhs.m_has_val) {
2880
259k
      geterr().~unexpected<E>();
2881
259k
      construct(std::move(rhs).get());
2882
259k
    } else {
2883
0
      assign_common(std::move(rhs));
2884
0
    }
2885
259k
  }
2886
2887
  template <class U = T,
2888
            detail::enable_if_t<
2889
                !std::is_nothrow_move_constructible<U>::value>* = nullptr>
2890
  void assign(expected_operations_base&& rhs) {
2891
    if (!this->m_has_val && rhs.m_has_val) {
2892
      auto tmp = std::move(geterr());
2893
      geterr().~unexpected<E>();
2894
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
2895
      try {
2896
        construct(std::move(rhs).get());
2897
      } catch (...) {
2898
        geterr() = std::move(tmp);
2899
        throw;
2900
      }
2901
#else
2902
      construct(std::move(rhs).get());
2903
#endif
2904
    } else {
2905
      assign_common(std::move(rhs));
2906
    }
2907
  }
2908
2909
#else
2910
2911
  // If exceptions are disabled then we can just copy-construct
2912
  void assign(const expected_operations_base& rhs) noexcept {
2913
    if (!this->m_has_val && rhs.m_has_val) {
2914
      geterr().~unexpected<E>();
2915
      construct(rhs.get());
2916
    } else {
2917
      assign_common(rhs);
2918
    }
2919
  }
2920
2921
  void assign(expected_operations_base&& rhs) noexcept {
2922
    if (!this->m_has_val && rhs.m_has_val) {
2923
      geterr().~unexpected<E>();
2924
      construct(std::move(rhs).get());
2925
    } else {
2926
      assign_common(std::move(rhs));
2927
    }
2928
  }
2929
2930
#endif
2931
2932
  // The common part of move/copy assigning
2933
  template <class Rhs>
2934
0
  void assign_common(Rhs&& rhs) {
2935
0
    if (this->m_has_val) {
2936
0
      if (rhs.m_has_val) {
2937
0
        get() = std::forward<Rhs>(rhs).get();
2938
0
      } else {
2939
0
        destroy_val();
2940
0
        construct_error(std::forward<Rhs>(rhs).geterr());
2941
0
      }
2942
0
    } else {
2943
0
      if (!rhs.m_has_val) {
2944
0
        geterr() = std::forward<Rhs>(rhs).geterr();
2945
0
      }
2946
0
    }
2947
0
  }
2948
2949
0
  bool has_value() const { return this->m_has_val; }
2950
2951
0
  TL_EXPECTED_11_CONSTEXPR T& get() & { return this->m_val; }
2952
0
  constexpr const T& get() const& { return this->m_val; }
2953
259k
  TL_EXPECTED_11_CONSTEXPR T&& get() && { return std::move(this->m_val); }
2954
#ifndef TL_EXPECTED_NO_CONSTRR
2955
  constexpr const T&& get() const&& { return std::move(this->m_val); }
2956
#endif
2957
2958
259k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
2959
259k
    return this->m_unexpect;
2960
259k
  }
2961
0
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
2962
0
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
2963
0
    return std::move(this->m_unexpect);
2964
0
  }
2965
#ifndef TL_EXPECTED_NO_CONSTRR
2966
  constexpr const unexpected<E>&& geterr() const&& {
2967
    return std::move(this->m_unexpect);
2968
  }
2969
#endif
2970
2971
0
  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
2972
};
2973
2974
// This base class provides some handy member functions which can be used in
2975
// further derived classes
2976
template <class E>
2977
struct expected_operations_base<void, E> : expected_storage_base<void, E> {
2978
  using expected_storage_base<void, E>::expected_storage_base;
2979
2980
  template <class... Args>
2981
  void construct() noexcept {
2982
    this->m_has_val = true;
2983
  }
2984
2985
  // This function doesn't use its argument, but needs it so that code in
2986
  // levels above this can work independently of whether T is void
2987
  template <class Rhs>
2988
  void construct_with(Rhs&&) noexcept {
2989
    this->m_has_val = true;
2990
  }
2991
2992
  template <class... Args>
2993
  void construct_error(Args&&... args) noexcept {
2994
    new (std::addressof(this->m_unexpect))
2995
        unexpected<E>(std::forward<Args>(args)...);
2996
    this->m_has_val = false;
2997
  }
2998
2999
  template <class Rhs>
3000
  void assign(Rhs&& rhs) noexcept {
3001
    if (!this->m_has_val) {
3002
      if (rhs.m_has_val) {
3003
        geterr().~unexpected<E>();
3004
        construct();
3005
      } else {
3006
        geterr() = std::forward<Rhs>(rhs).geterr();
3007
      }
3008
    } else {
3009
      if (!rhs.m_has_val) {
3010
        construct_error(std::forward<Rhs>(rhs).geterr());
3011
      }
3012
    }
3013
  }
3014
3015
  bool has_value() const { return this->m_has_val; }
3016
3017
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& geterr() & {
3018
    return this->m_unexpect;
3019
  }
3020
  constexpr const unexpected<E>& geterr() const& { return this->m_unexpect; }
3021
  TL_EXPECTED_11_CONSTEXPR unexpected<E>&& geterr() && {
3022
    return std::move(this->m_unexpect);
3023
  }
3024
#ifndef TL_EXPECTED_NO_CONSTRR
3025
  constexpr const unexpected<E>&& geterr() const&& {
3026
    return std::move(this->m_unexpect);
3027
  }
3028
#endif
3029
3030
  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
3031
    // no-op
3032
  }
3033
};
3034
3035
// This class manages conditionally having a trivial copy constructor
3036
// This specialization is for when T and E are trivially copy constructible
3037
template <class T, class E,
3038
          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(
3039
                                   T)>::value &&
3040
                 TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
3041
struct expected_copy_base : expected_operations_base<T, E> {
3042
  using expected_operations_base<T, E>::expected_operations_base;
3043
};
3044
3045
// This specialization is for when T or E are not trivially copy constructible
3046
template <class T, class E>
3047
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
3048
  using expected_operations_base<T, E>::expected_operations_base;
3049
3050
  expected_copy_base() = default;
3051
  expected_copy_base(const expected_copy_base& rhs)
3052
0
      : expected_operations_base<T, E>(no_init) {
3053
0
    if (rhs.has_value()) {
3054
0
      this->construct_with(rhs);
3055
0
    } else {
3056
0
      this->construct_error(rhs.geterr());
3057
0
    }
3058
0
  }
3059
3060
  expected_copy_base(expected_copy_base&& rhs) = default;
3061
  expected_copy_base& operator=(const expected_copy_base& rhs) = default;
3062
  expected_copy_base& operator=(expected_copy_base&& rhs) = default;
3063
};
3064
3065
// This class manages conditionally having a trivial move constructor
3066
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3067
// doesn't implement an analogue to std::is_trivially_move_constructible. We
3068
// have to make do with a non-trivial move constructor even if T is trivially
3069
// move constructible
3070
#ifndef TL_EXPECTED_GCC49
3071
template <class T, class E,
3072
          bool =
3073
              is_void_or<T, std::is_trivially_move_constructible<T>>::value &&
3074
              std::is_trivially_move_constructible<E>::value>
3075
struct expected_move_base : expected_copy_base<T, E> {
3076
  using expected_copy_base<T, E>::expected_copy_base;
3077
};
3078
#else
3079
template <class T, class E, bool = false>
3080
struct expected_move_base;
3081
#endif
3082
template <class T, class E>
3083
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
3084
  using expected_copy_base<T, E>::expected_copy_base;
3085
3086
  expected_move_base() = default;
3087
0
  expected_move_base(const expected_move_base& rhs) = default;
3088
3089
  expected_move_base(expected_move_base&& rhs) noexcept(
3090
      std::is_nothrow_move_constructible<T>::value)
3091
      : expected_copy_base<T, E>(no_init) {
3092
    if (rhs.has_value()) {
3093
      this->construct_with(std::move(rhs));
3094
    } else {
3095
      this->construct_error(std::move(rhs.geterr()));
3096
    }
3097
  }
3098
  expected_move_base& operator=(const expected_move_base& rhs) = default;
3099
  expected_move_base& operator=(expected_move_base&& rhs) = default;
3100
};
3101
3102
// This class manages conditionally having a trivial copy assignment operator
3103
template <
3104
    class T, class E,
3105
    bool =
3106
        is_void_or<
3107
            T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
3108
                           TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
3109
                           TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value &&
3110
        TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value &&
3111
        TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value &&
3112
        TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
3113
struct expected_copy_assign_base : expected_move_base<T, E> {
3114
  using expected_move_base<T, E>::expected_move_base;
3115
};
3116
3117
template <class T, class E>
3118
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
3119
  using expected_move_base<T, E>::expected_move_base;
3120
3121
  expected_copy_assign_base() = default;
3122
0
  expected_copy_assign_base(const expected_copy_assign_base& rhs) = default;
3123
3124
  expected_copy_assign_base(expected_copy_assign_base&& rhs) = default;
3125
  expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
3126
    this->assign(rhs);
3127
    return *this;
3128
  }
3129
  expected_copy_assign_base& operator=(expected_copy_assign_base&& rhs) =
3130
      default;
3131
};
3132
3133
// This class manages conditionally having a trivial move assignment operator
3134
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
3135
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
3136
// to make do with a non-trivial move assignment operator even if T is trivially
3137
// move assignable
3138
#ifndef TL_EXPECTED_GCC49
3139
template <
3140
    class T, class E,
3141
    bool = is_void_or<
3142
               T, conjunction<std::is_trivially_destructible<T>,
3143
                              std::is_trivially_move_constructible<T>,
3144
                              std::is_trivially_move_assignable<T>>>::value &&
3145
           std::is_trivially_destructible<E>::value &&
3146
           std::is_trivially_move_constructible<E>::value &&
3147
           std::is_trivially_move_assignable<E>::value>
3148
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
3149
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3150
};
3151
#else
3152
template <class T, class E, bool = false>
3153
struct expected_move_assign_base;
3154
#endif
3155
3156
template <class T, class E>
3157
struct expected_move_assign_base<T, E, false>
3158
    : expected_copy_assign_base<T, E> {
3159
  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
3160
3161
  expected_move_assign_base() = default;
3162
0
  expected_move_assign_base(const expected_move_assign_base& rhs) = default;
3163
3164
  expected_move_assign_base(expected_move_assign_base&& rhs) = default;
3165
3166
  expected_move_assign_base& operator=(const expected_move_assign_base& rhs) =
3167
      default;
3168
3169
  expected_move_assign_base&
3170
  operator=(expected_move_assign_base&& rhs) noexcept(
3171
      std::is_nothrow_move_constructible<T>::value &&
3172
259k
      std::is_nothrow_move_assignable<T>::value) {
3173
259k
    this->assign(std::move(rhs));
3174
259k
    return *this;
3175
259k
  }
3176
};
3177
3178
// expected_delete_ctor_base will conditionally delete copy and move
3179
// constructors depending on whether T is copy/move constructible
3180
template <class T, class E,
3181
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3182
                             std::is_copy_constructible<E>::value),
3183
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3184
                             std::is_move_constructible<E>::value)>
3185
struct expected_delete_ctor_base {
3186
  expected_delete_ctor_base() = default;
3187
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3188
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3189
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3190
      default;
3191
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3192
      default;
3193
};
3194
3195
template <class T, class E>
3196
struct expected_delete_ctor_base<T, E, true, false> {
3197
  expected_delete_ctor_base() = default;
3198
  expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
3199
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3200
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3201
      default;
3202
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3203
      default;
3204
};
3205
3206
template <class T, class E>
3207
struct expected_delete_ctor_base<T, E, false, true> {
3208
  expected_delete_ctor_base() = default;
3209
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3210
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
3211
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3212
      default;
3213
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3214
      default;
3215
};
3216
3217
template <class T, class E>
3218
struct expected_delete_ctor_base<T, E, false, false> {
3219
  expected_delete_ctor_base() = default;
3220
  expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
3221
  expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
3222
  expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) =
3223
      default;
3224
  expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept =
3225
      default;
3226
};
3227
3228
// expected_delete_assign_base will conditionally delete copy and move
3229
// constructors depending on whether T and E are copy/move constructible +
3230
// assignable
3231
template <class T, class E,
3232
          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
3233
                             std::is_copy_constructible<E>::value &&
3234
                             is_copy_assignable_or_void<T>::value &&
3235
                             std::is_copy_assignable<E>::value),
3236
          bool EnableMove = (is_move_constructible_or_void<T>::value &&
3237
                             std::is_move_constructible<E>::value &&
3238
                             is_move_assignable_or_void<T>::value &&
3239
                             std::is_move_assignable<E>::value)>
3240
struct expected_delete_assign_base {
3241
  expected_delete_assign_base() = default;
3242
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3243
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3244
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3245
      default;
3246
  expected_delete_assign_base& operator=(
3247
      expected_delete_assign_base&&) noexcept = default;
3248
};
3249
3250
template <class T, class E>
3251
struct expected_delete_assign_base<T, E, true, false> {
3252
  expected_delete_assign_base() = default;
3253
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3254
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3255
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3256
      default;
3257
  expected_delete_assign_base& operator=(
3258
      expected_delete_assign_base&&) noexcept = delete;
3259
};
3260
3261
template <class T, class E>
3262
struct expected_delete_assign_base<T, E, false, true> {
3263
  expected_delete_assign_base() = default;
3264
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3265
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3266
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3267
      delete;
3268
  expected_delete_assign_base& operator=(
3269
      expected_delete_assign_base&&) noexcept = default;
3270
};
3271
3272
template <class T, class E>
3273
struct expected_delete_assign_base<T, E, false, false> {
3274
  expected_delete_assign_base() = default;
3275
  expected_delete_assign_base(const expected_delete_assign_base&) = default;
3276
  expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
3277
  expected_delete_assign_base& operator=(const expected_delete_assign_base&) =
3278
      delete;
3279
  expected_delete_assign_base& operator=(
3280
      expected_delete_assign_base&&) noexcept = delete;
3281
};
3282
3283
// This is needed to be able to construct the expected_default_ctor_base which
3284
// follows, while still conditionally deleting the default constructor.
3285
struct default_constructor_tag {
3286
  explicit constexpr default_constructor_tag() = default;
3287
};
3288
3289
// expected_default_ctor_base will ensure that expected has a deleted default
3290
// consturctor if T is not default constructible.
3291
// This specialization is for when T is default constructible
3292
template <class T, class E,
3293
          bool Enable =
3294
              std::is_default_constructible<T>::value || std::is_void<T>::value>
3295
struct expected_default_ctor_base {
3296
  constexpr expected_default_ctor_base() noexcept = default;
3297
  constexpr expected_default_ctor_base(
3298
      expected_default_ctor_base const&) noexcept = default;
3299
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3300
      default;
3301
  expected_default_ctor_base& operator=(
3302
      expected_default_ctor_base const&) noexcept = default;
3303
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3304
      default;
3305
3306
9.37M
  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)
tl::detail::expected_default_ctor_base<ada::url_aggregator, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
2.09M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern_init, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
543k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
2.17M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
1.20M
  constexpr explicit expected_default_ctor_base(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)
tl::detail::expected_default_ctor_base<bool, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
388k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::optional<ada::url_pattern_result>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
518k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
1.11M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
240k
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
tl::detail::expected_default_ctor_base<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors, true>::expected_default_ctor_base(tl::detail::default_constructor_tag)
Line
Count
Source
3306
1.11M
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3307
};
3308
3309
// This specialization is for when T is not default constructible
3310
template <class T, class E>
3311
struct expected_default_ctor_base<T, E, false> {
3312
  constexpr expected_default_ctor_base() noexcept = delete;
3313
  constexpr expected_default_ctor_base(
3314
      expected_default_ctor_base const&) noexcept = default;
3315
  constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept =
3316
      default;
3317
  expected_default_ctor_base& operator=(
3318
      expected_default_ctor_base const&) noexcept = default;
3319
  expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept =
3320
      default;
3321
3322
  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
3323
};
3324
}  // namespace detail
3325
3326
template <class E>
3327
class bad_expected_access : public std::exception {
3328
 public:
3329
0
  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
3330
3331
0
  virtual const char* what() const noexcept override {
3332
0
    return "Bad expected access";
3333
0
  }
3334
3335
  const E& error() const& { return m_val; }
3336
  E& error() & { return m_val; }
3337
  const E&& error() const&& { return std::move(m_val); }
3338
  E&& error() && { return std::move(m_val); }
3339
3340
 private:
3341
  E m_val;
3342
};
3343
3344
/// An `expected<T, E>` object is an object that contains the storage for
3345
/// another object and manages the lifetime of this contained object `T`.
3346
/// Alternatively it could contain the storage for another unexpected object
3347
/// `E`. The contained object may not be initialized after the expected object
3348
/// has been initialized, and may not be destroyed before the expected object
3349
/// has been destroyed. The initialization state of the contained object is
3350
/// tracked by the expected object.
3351
template <class T, class E>
3352
class expected : private detail::expected_move_assign_base<T, E>,
3353
                 private detail::expected_delete_ctor_base<T, E>,
3354
                 private detail::expected_delete_assign_base<T, E>,
3355
                 private detail::expected_default_ctor_base<T, E> {
3356
  static_assert(!std::is_reference<T>::value, "T must not be a reference");
3357
  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
3358
                "T must not be in_place_t");
3359
  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
3360
                "T must not be unexpect_t");
3361
  static_assert(
3362
      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
3363
      "T must not be unexpected<E>");
3364
  static_assert(!std::is_reference<E>::value, "E must not be a reference");
3365
3366
11.1M
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::valptr()
tl::expected<ada::url_aggregator, ada::errors>::valptr()
Line
Count
Source
3366
2.57M
  T* valptr() { return std::addressof(this->m_val); }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::valptr()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::valptr()
tl::expected<ada::url_pattern_init, ada::errors>::valptr()
Line
Count
Source
3366
5.31M
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::valptr()
Line
Count
Source
3366
777k
  T* valptr() { return std::addressof(this->m_val); }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::valptr()
Line
Count
Source
3366
2.47M
  T* valptr() { return std::addressof(this->m_val); }
3367
  const T* valptr() const { return std::addressof(this->m_val); }
3368
  unexpected<E>* errptr() { return std::addressof(this->m_unexpect); }
3369
  const unexpected<E>* errptr() const {
3370
    return std::addressof(this->m_unexpect);
3371
  }
3372
3373
  template <class U = T,
3374
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3375
9.19M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
9.19M
    return this->m_val;
3377
9.19M
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3375
320k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
320k
    return this->m_val;
3377
320k
  }
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3375
2.16M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
2.16M
    return this->m_val;
3377
2.16M
  }
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEE3valIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
3375
3.62M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
3.62M
    return this->m_val;
3377
3.62M
  }
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3375
259k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
259k
    return this->m_val;
3377
259k
  }
_ZN2tl8expectedIbN3ada6errorsEE3valIbTnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS7_v
Line
Count
Source
3375
388k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
388k
    return this->m_val;
3377
388k
  }
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEE3valIS5_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSA_v
Line
Count
Source
3375
46.4k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
46.4k
    return this->m_val;
3377
46.4k
  }
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEE3valIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
3375
1.19M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
1.19M
    return this->m_val;
3377
1.19M
  }
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
3375
88.6k
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
88.6k
    return this->m_val;
3377
88.6k
  }
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEE3valIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
3375
1.10M
  TL_EXPECTED_11_CONSTEXPR U& val() {
3376
1.10M
    return this->m_val;
3377
1.10M
  }
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEE3valIS3_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS9_v
3378
80.9k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::err()
Line
Count
Source
3378
5.04k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::err()
Line
Count
Source
3378
7.11k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<ada::url_aggregator, ada::errors>::err()
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::err()
Line
Count
Source
3378
1.50k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_init, ada::errors>::err()
Line
Count
Source
3378
59.0k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::err()
Line
Count
Source
3378
8.28k
  TL_EXPECTED_11_CONSTEXPR unexpected<E>& err() { return this->m_unexpect; }
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::err()
3379
3380
  template <class U = T,
3381
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
3382
  constexpr const U& val() const {
3383
    return this->m_val;
3384
  }
3385
  constexpr const unexpected<E>& err() const { return this->m_unexpect; }
3386
3387
  using impl_base = detail::expected_move_assign_base<T, E>;
3388
  using ctor_base = detail::expected_default_ctor_base<T, E>;
3389
3390
 public:
3391
  typedef T value_type;
3392
  typedef E error_type;
3393
  typedef unexpected<E> unexpected_type;
3394
3395
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3396
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3397
  template <class F>
3398
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & {
3399
    return and_then_impl(*this, std::forward<F>(f));
3400
  }
3401
  template <class F>
3402
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && {
3403
    return and_then_impl(std::move(*this), std::forward<F>(f));
3404
  }
3405
  template <class F>
3406
  constexpr auto and_then(F&& f) const& {
3407
    return and_then_impl(*this, std::forward<F>(f));
3408
  }
3409
3410
#ifndef TL_EXPECTED_NO_CONSTRR
3411
  template <class F>
3412
  constexpr auto and_then(F&& f) const&& {
3413
    return and_then_impl(std::move(*this), std::forward<F>(f));
3414
  }
3415
#endif
3416
3417
#else
3418
  template <class F>
3419
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) & -> decltype(and_then_impl(
3420
      std::declval<expected&>(), std::forward<F>(f))) {
3421
    return and_then_impl(*this, std::forward<F>(f));
3422
  }
3423
  template <class F>
3424
  TL_EXPECTED_11_CONSTEXPR auto and_then(F&& f) && -> decltype(and_then_impl(
3425
      std::declval<expected&&>(), std::forward<F>(f))) {
3426
    return and_then_impl(std::move(*this), std::forward<F>(f));
3427
  }
3428
  template <class F>
3429
  constexpr auto and_then(F&& f) const& -> decltype(and_then_impl(
3430
      std::declval<expected const&>(), std::forward<F>(f))) {
3431
    return and_then_impl(*this, std::forward<F>(f));
3432
  }
3433
3434
#ifndef TL_EXPECTED_NO_CONSTRR
3435
  template <class F>
3436
  constexpr auto and_then(F&& f) const&& -> decltype(and_then_impl(
3437
      std::declval<expected const&&>(), std::forward<F>(f))) {
3438
    return and_then_impl(std::move(*this), std::forward<F>(f));
3439
  }
3440
#endif
3441
#endif
3442
3443
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3444
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3445
  template <class F>
3446
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) & {
3447
    return expected_map_impl(*this, std::forward<F>(f));
3448
  }
3449
  template <class F>
3450
  TL_EXPECTED_11_CONSTEXPR auto map(F&& f) && {
3451
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3452
  }
3453
  template <class F>
3454
  constexpr auto map(F&& f) const& {
3455
    return expected_map_impl(*this, std::forward<F>(f));
3456
  }
3457
  template <class F>
3458
  constexpr auto map(F&& f) const&& {
3459
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3460
  }
3461
#else
3462
  template <class F>
3463
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3464
      std::declval<expected&>(), std::declval<F&&>())) map(F&& f) & {
3465
    return expected_map_impl(*this, std::forward<F>(f));
3466
  }
3467
  template <class F>
3468
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3469
                                                      std::declval<F&&>()))
3470
  map(F&& f) && {
3471
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3472
  }
3473
  template <class F>
3474
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3475
                                       std::declval<F&&>()))
3476
  map(F&& f) const& {
3477
    return expected_map_impl(*this, std::forward<F>(f));
3478
  }
3479
3480
#ifndef TL_EXPECTED_NO_CONSTRR
3481
  template <class F>
3482
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3483
                                       std::declval<F&&>())) map(F&& f)
3484
      const&& {
3485
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3486
  }
3487
#endif
3488
#endif
3489
3490
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3491
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3492
  template <class F>
3493
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) & {
3494
    return expected_map_impl(*this, std::forward<F>(f));
3495
  }
3496
  template <class F>
3497
  TL_EXPECTED_11_CONSTEXPR auto transform(F&& f) && {
3498
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3499
  }
3500
  template <class F>
3501
  constexpr auto transform(F&& f) const& {
3502
    return expected_map_impl(*this, std::forward<F>(f));
3503
  }
3504
  template <class F>
3505
  constexpr auto transform(F&& f) const&& {
3506
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3507
  }
3508
#else
3509
  template <class F>
3510
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
3511
      std::declval<expected&>(), std::declval<F&&>())) transform(F&& f) & {
3512
    return expected_map_impl(*this, std::forward<F>(f));
3513
  }
3514
  template <class F>
3515
  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
3516
                                                      std::declval<F&&>()))
3517
  transform(F&& f) && {
3518
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3519
  }
3520
  template <class F>
3521
  constexpr decltype(expected_map_impl(std::declval<const expected&>(),
3522
                                       std::declval<F&&>()))
3523
  transform(F&& f) const& {
3524
    return expected_map_impl(*this, std::forward<F>(f));
3525
  }
3526
3527
#ifndef TL_EXPECTED_NO_CONSTRR
3528
  template <class F>
3529
  constexpr decltype(expected_map_impl(std::declval<const expected&&>(),
3530
                                       std::declval<F&&>())) transform(F&& f)
3531
      const&& {
3532
    return expected_map_impl(std::move(*this), std::forward<F>(f));
3533
  }
3534
#endif
3535
#endif
3536
3537
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3538
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3539
  template <class F>
3540
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) & {
3541
    return map_error_impl(*this, std::forward<F>(f));
3542
  }
3543
  template <class F>
3544
  TL_EXPECTED_11_CONSTEXPR auto map_error(F&& f) && {
3545
    return map_error_impl(std::move(*this), std::forward<F>(f));
3546
  }
3547
  template <class F>
3548
  constexpr auto map_error(F&& f) const& {
3549
    return map_error_impl(*this, std::forward<F>(f));
3550
  }
3551
  template <class F>
3552
  constexpr auto map_error(F&& f) const&& {
3553
    return map_error_impl(std::move(*this), std::forward<F>(f));
3554
  }
3555
#else
3556
  template <class F>
3557
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3558
      std::declval<expected&>(), std::declval<F&&>())) map_error(F&& f) & {
3559
    return map_error_impl(*this, std::forward<F>(f));
3560
  }
3561
  template <class F>
3562
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3563
                                                   std::declval<F&&>()))
3564
  map_error(F&& f) && {
3565
    return map_error_impl(std::move(*this), std::forward<F>(f));
3566
  }
3567
  template <class F>
3568
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3569
                                    std::declval<F&&>()))
3570
  map_error(F&& f) const& {
3571
    return map_error_impl(*this, std::forward<F>(f));
3572
  }
3573
3574
#ifndef TL_EXPECTED_NO_CONSTRR
3575
  template <class F>
3576
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3577
                                    std::declval<F&&>())) map_error(F&& f)
3578
      const&& {
3579
    return map_error_impl(std::move(*this), std::forward<F>(f));
3580
  }
3581
#endif
3582
#endif
3583
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
3584
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
3585
  template <class F>
3586
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) & {
3587
    return map_error_impl(*this, std::forward<F>(f));
3588
  }
3589
  template <class F>
3590
  TL_EXPECTED_11_CONSTEXPR auto transform_error(F&& f) && {
3591
    return map_error_impl(std::move(*this), std::forward<F>(f));
3592
  }
3593
  template <class F>
3594
  constexpr auto transform_error(F&& f) const& {
3595
    return map_error_impl(*this, std::forward<F>(f));
3596
  }
3597
  template <class F>
3598
  constexpr auto transform_error(F&& f) const&& {
3599
    return map_error_impl(std::move(*this), std::forward<F>(f));
3600
  }
3601
#else
3602
  template <class F>
3603
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(
3604
      std::declval<expected&>(),
3605
      std::declval<F&&>())) transform_error(F&& f) & {
3606
    return map_error_impl(*this, std::forward<F>(f));
3607
  }
3608
  template <class F>
3609
  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected&&>(),
3610
                                                   std::declval<F&&>()))
3611
  transform_error(F&& f) && {
3612
    return map_error_impl(std::move(*this), std::forward<F>(f));
3613
  }
3614
  template <class F>
3615
  constexpr decltype(map_error_impl(std::declval<const expected&>(),
3616
                                    std::declval<F&&>()))
3617
  transform_error(F&& f) const& {
3618
    return map_error_impl(*this, std::forward<F>(f));
3619
  }
3620
3621
#ifndef TL_EXPECTED_NO_CONSTRR
3622
  template <class F>
3623
  constexpr decltype(map_error_impl(std::declval<const expected&&>(),
3624
                                    std::declval<F&&>())) transform_error(F&& f)
3625
      const&& {
3626
    return map_error_impl(std::move(*this), std::forward<F>(f));
3627
  }
3628
#endif
3629
#endif
3630
  template <class F>
3631
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) & {
3632
    return or_else_impl(*this, std::forward<F>(f));
3633
  }
3634
3635
  template <class F>
3636
  expected TL_EXPECTED_11_CONSTEXPR or_else(F&& f) && {
3637
    return or_else_impl(std::move(*this), std::forward<F>(f));
3638
  }
3639
3640
  template <class F>
3641
  expected constexpr or_else(F&& f) const& {
3642
    return or_else_impl(*this, std::forward<F>(f));
3643
  }
3644
3645
#ifndef TL_EXPECTED_NO_CONSTRR
3646
  template <class F>
3647
  expected constexpr or_else(F&& f) const&& {
3648
    return or_else_impl(std::move(*this), std::forward<F>(f));
3649
  }
3650
#endif
3651
  constexpr expected() = default;
3652
0
  constexpr expected(const expected& rhs) = default;
3653
  constexpr expected(expected&& rhs) = default;
3654
  expected& operator=(const expected& rhs) = default;
3655
259k
  expected& operator=(expected&& rhs) = default;
3656
3657
  template <class... Args,
3658
            detail::enable_if_t<std::is_constructible<T, Args&&...>::value>* =
3659
                nullptr>
3660
  constexpr expected(in_place_t, Args&&... args)
3661
7.93M
      : impl_base(in_place, std::forward<Args>(args)...),
3662
7.93M
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3661
844k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
844k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_
Line
Count
Source
3661
395k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
395k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3661
1.02M
      : impl_base(in_place, std::forward<Args>(args)...),
3662
1.02M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA1_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3661
1.13M
      : impl_base(in_place, std::forward<Args>(args)...),
3662
1.13M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IJRA2_KcETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESI_
Line
Count
Source
3661
49
      : impl_base(in_place, std::forward<Args>(args)...),
3662
49
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IJRS8_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS8_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESG_
Line
Count
Source
3661
1.19M
      : impl_base(in_place, std::forward<Args>(args)...),
3662
1.19M
        ctor_base(detail::default_constructor_tag{}) {}
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_
_ZN2tl8expectedIbN3ada6errorsEEC2IJbETnPNSt3__19enable_ifIXsr3std16is_constructibleIbDpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tES9_
Line
Count
Source
3661
388k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
388k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IJRKNS1_9nullopt_tEETnPNS1_9enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3661
462k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
462k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IJS4_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESC_
Line
Count
Source
3661
55.6k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
55.6k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IJRS7_ETnPNS1_9enable_ifIXsr3std16is_constructibleIS7_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESF_
Line
Count
Source
3661
1.10M
      : impl_base(in_place, std::forward<Args>(args)...),
3662
1.10M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IJRS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_
Line
Count
Source
3661
88.6k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
88.6k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
3661
1.10M
      : impl_base(in_place, std::forward<Args>(args)...),
3662
1.10M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IJS5_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS5_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESD_
Line
Count
Source
3661
129k
      : impl_base(in_place, std::forward<Args>(args)...),
3662
129k
        ctor_base(detail::default_constructor_tag{}) {}
3663
3664
  template <class U, class... Args,
3665
            detail::enable_if_t<std::is_constructible<
3666
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3667
  constexpr expected(in_place_t, std::initializer_list<U> il, Args&&... args)
3668
      : impl_base(in_place, il, std::forward<Args>(args)...),
3669
        ctor_base(detail::default_constructor_tag{}) {}
3670
3671
  template <
3672
      class G = E,
3673
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3674
      detail::enable_if_t<!std::is_convertible<const G&, E>::value>* = nullptr>
3675
  explicit constexpr expected(const unexpected<G>& e)
3676
      : impl_base(unexpect, e.value()),
3677
        ctor_base(detail::default_constructor_tag{}) {}
3678
3679
  template <
3680
      class G = E,
3681
      detail::enable_if_t<std::is_constructible<E, const G&>::value>* = nullptr,
3682
      detail::enable_if_t<std::is_convertible<const G&, E>::value>* = nullptr>
3683
  constexpr expected(unexpected<G> const& e)
3684
      : impl_base(unexpect, e.value()),
3685
        ctor_base(detail::default_constructor_tag{}) {}
3686
3687
  template <
3688
      class G = E,
3689
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3690
      detail::enable_if_t<!std::is_convertible<G&&, E>::value>* = nullptr>
3691
  explicit constexpr expected(unexpected<G>&& e) noexcept(
3692
      std::is_nothrow_constructible<E, G&&>::value)
3693
      : impl_base(unexpect, std::move(e.value())),
3694
        ctor_base(detail::default_constructor_tag{}) {}
3695
3696
  template <
3697
      class G = E,
3698
      detail::enable_if_t<std::is_constructible<E, G&&>::value>* = nullptr,
3699
      detail::enable_if_t<std::is_convertible<G&&, E>::value>* = nullptr>
3700
  constexpr expected(unexpected<G>&& e) noexcept(
3701
      std::is_nothrow_constructible<E, G&&>::value)
3702
1.44M
      : impl_base(unexpect, std::move(e.value())),
3703
1.44M
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3702
1.25M
      : impl_base(unexpect, std::move(e.value())),
3703
1.25M
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE
Line
Count
Source
3702
59.6k
      : impl_base(unexpect, std::move(e.value())),
3703
59.6k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Line
Count
Source
3702
5.04k
      : impl_base(unexpect, std::move(e.value())),
3703
5.04k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IS9_TnPNS1_9enable_ifIXsr3std16is_constructibleIS9_OT_EE5valueEvE4typeELPv0ETnPNSC_IXsr3std14is_convertibleISE_S9_EE5valueEvE4typeELSI_0EEEONS_10unexpectedISD_EE
Line
Count
Source
3702
1.50k
      : impl_base(unexpect, std::move(e.value())),
3703
1.50k
        ctor_base(detail::default_constructor_tag{}) {}
Unexecuted instantiation: _ZN2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEC2IS4_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_OT_EE5valueEvE4typeELPv0ETnPNS8_IXsr3std14is_convertibleISA_S4_EE5valueEvE4typeELSE_0EEEONS_10unexpectedIS9_EE
Unexecuted instantiation: _ZN2tl8expectedIbN3ada6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_OT_EE5valueEvE4typeELPv0ETnPNS6_IXsr3std14is_convertibleIS8_S2_EE5valueEvE4typeELSC_0EEEONS_10unexpectedIS7_EE
Unexecuted instantiation: _ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IS6_TnPNS1_9enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNS9_IXsr3std14is_convertibleISB_S6_EE5valueEvE4typeELSF_0EEEONS_10unexpectedISA_EE
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IS8_TnPNS1_9enable_ifIXsr3std16is_constructibleIS8_OT_EE5valueEvE4typeELPv0ETnPNSB_IXsr3std14is_convertibleISD_S8_EE5valueEvE4typeELSH_0EEEONS_10unexpectedISC_EE
Line
Count
Source
3702
7.11k
      : impl_base(unexpect, std::move(e.value())),
3703
7.11k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS6_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNSA_IXsr3std14is_convertibleISC_S6_EE5valueEvE4typeELSG_0EEEONS_10unexpectedISB_EE
Line
Count
Source
3702
110k
      : impl_base(unexpect, std::move(e.value())),
3703
110k
        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS6_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS6_OT_EE5valueEvE4typeELPv0ETnPNSA_IXsr3std14is_convertibleISC_S6_EE5valueEvE4typeELSG_0EEEONS_10unexpectedISB_EE
Line
Count
Source
3702
8.28k
      : impl_base(unexpect, std::move(e.value())),
3703
8.28k
        ctor_base(detail::default_constructor_tag{}) {}
3704
3705
  template <class... Args,
3706
            detail::enable_if_t<std::is_constructible<E, Args&&...>::value>* =
3707
                nullptr>
3708
  constexpr explicit expected(unexpect_t, Args&&... args)
3709
      : impl_base(unexpect, std::forward<Args>(args)...),
3710
        ctor_base(detail::default_constructor_tag{}) {}
3711
3712
  template <class U, class... Args,
3713
            detail::enable_if_t<std::is_constructible<
3714
                E, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3715
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
3716
                              Args&&... args)
3717
      : impl_base(unexpect, il, std::forward<Args>(args)...),
3718
        ctor_base(detail::default_constructor_tag{}) {}
3719
3720
  template <class U, class G,
3721
            detail::enable_if_t<!(std::is_convertible<U const&, T>::value &&
3722
                                  std::is_convertible<G const&, E>::value)>* =
3723
                nullptr,
3724
            detail::expected_enable_from_other<T, E, U, G, const U&,
3725
                                               const G&>* = nullptr>
3726
  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3727
      : ctor_base(detail::default_constructor_tag{}) {
3728
    if (rhs.has_value()) {
3729
      this->construct(*rhs);
3730
    } else {
3731
      this->construct_error(rhs.error());
3732
    }
3733
  }
3734
3735
  template <
3736
      class U, class G,
3737
      detail::enable_if_t<(std::is_convertible<U const&, T>::value &&
3738
                           std::is_convertible<G const&, E>::value)>* = nullptr,
3739
      detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* =
3740
          nullptr>
3741
  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G>& rhs)
3742
      : ctor_base(detail::default_constructor_tag{}) {
3743
    if (rhs.has_value()) {
3744
      this->construct(*rhs);
3745
    } else {
3746
      this->construct_error(rhs.error());
3747
    }
3748
  }
3749
3750
  template <
3751
      class U, class G,
3752
      detail::enable_if_t<!(std::is_convertible<U&&, T>::value &&
3753
                            std::is_convertible<G&&, E>::value)>* = nullptr,
3754
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3755
  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3756
      : ctor_base(detail::default_constructor_tag{}) {
3757
    if (rhs.has_value()) {
3758
      this->construct(std::move(*rhs));
3759
    } else {
3760
      this->construct_error(std::move(rhs.error()));
3761
    }
3762
  }
3763
3764
  template <
3765
      class U, class G,
3766
      detail::enable_if_t<(std::is_convertible<U&&, T>::value &&
3767
                           std::is_convertible<G&&, E>::value)>* = nullptr,
3768
      detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
3769
  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G>&& rhs)
3770
      : ctor_base(detail::default_constructor_tag{}) {
3771
    if (rhs.has_value()) {
3772
      this->construct(std::move(*rhs));
3773
    } else {
3774
      this->construct_error(std::move(rhs.error()));
3775
    }
3776
  }
3777
3778
  template <class U = T,
3779
            detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
3780
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3781
  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3782
      : expected(in_place, std::forward<U>(v)) {}
3783
3784
  template <class U = T,
3785
            detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
3786
            detail::expected_enable_forward_value<T, E, U>* = nullptr>
3787
  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U&& v)
3788
7.93M
      : 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_
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3788
844k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS7_IXaaaaaasr3std16is_constructibleIS2_S9_EE5valuentsr3std7is_sameINS6_5decayIS8_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SG_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESG_EE5valueEvE4typeELSD_0EEES9_
Line
Count
Source
3788
395k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3788
1.02M
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA1_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Line
Count
Source
3788
1.13M
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEC2IRA2_KcTnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSF_IXaaaaaasr3std16is_constructibleIS7_SH_EE5valuentsr3std7is_sameINS1_5decayISG_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SO_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESO_EE5valueEvE4typeELSL_0EEESH_
Line
Count
Source
3788
49
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEC2IRS8_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S8_EE5valueEvE4typeELPv0ETnPNSD_IXaaaaaasr3std16is_constructibleIS8_SF_EE5valuentsr3std7is_sameINS1_5decayISE_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameISA_SM_EE5valuentsr3std7is_sameINS_10unexpectedIS9_EESM_EE5valueEvE4typeELSJ_0EEESF_
Line
Count
Source
3788
1.19M
      : expected(in_place, std::forward<U>(v)) {}
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_
_ZN2tl8expectedIbN3ada6errorsEEC2IbTnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_bEE5valueEvE4typeELPv0ETnPNS6_IXaaaaaasr3std16is_constructibleIbS8_EE5valuentsr3std7is_sameINS5_5decayIS7_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS3_SF_EE5valuentsr3std7is_sameINS_10unexpectedIS2_EESF_EE5valueEvE4typeELSC_0EEES8_
Line
Count
Source
3788
388k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IRKNS1_9nullopt_tETnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS5_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3788
462k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEC2IS4_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNS9_IXaaaaaasr3std16is_constructibleIS5_SB_EE5valuentsr3std7is_sameINS1_5decayISA_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SI_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESI_EE5valueEvE4typeELSF_0EEESB_
Line
Count
Source
3788
55.6k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEC2IRS7_TnPNS1_9enable_ifIXsr3std14is_convertibleIOT_S7_EE5valueEvE4typeELPv0ETnPNSC_IXaaaaaasr3std16is_constructibleIS7_SE_EE5valuentsr3std7is_sameINS1_5decayISD_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS9_SL_EE5valuentsr3std7is_sameINS_10unexpectedIS8_EESL_EE5valueEvE4typeELSI_0EEESE_
Line
Count
Source
3788
1.10M
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEC2IRS2_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S2_EE5valueEvE4typeELPv0ETnPNS8_IXaaaaaasr3std16is_constructibleIS2_SA_EE5valuentsr3std7is_sameINS7_5decayIS9_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS4_SH_EE5valuentsr3std7is_sameINS_10unexpectedIS3_EESH_EE5valueEvE4typeELSE_0EEESA_
Line
Count
Source
3788
88.6k
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS5_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSA_IXaaaaaasr3std16is_constructibleIS5_SC_EE5valuentsr3std7is_sameINS9_5decayISB_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SJ_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESJ_EE5valueEvE4typeELSG_0EEESC_
Line
Count
Source
3788
1.10M
      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEC2IS5_TnPNSt3__19enable_ifIXsr3std14is_convertibleIOT_S5_EE5valueEvE4typeELPv0ETnPNSA_IXaaaaaasr3std16is_constructibleIS5_SC_EE5valuentsr3std7is_sameINS9_5decayISB_E4typeENS_10in_place_tEEE5valuentsr3std7is_sameIS7_SJ_EE5valuentsr3std7is_sameINS_10unexpectedIS6_EESJ_EE5valueEvE4typeELSG_0EEESC_
Line
Count
Source
3788
129k
      : expected(in_place, std::forward<U>(v)) {}
3789
3790
  template <
3791
      class U = T, class G = T,
3792
      detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* =
3793
          nullptr,
3794
      detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
3795
      detail::enable_if_t<
3796
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3797
           !detail::conjunction<std::is_scalar<T>,
3798
                                std::is_same<T, detail::decay_t<U>>>::value &&
3799
           std::is_constructible<T, U>::value &&
3800
           std::is_assignable<G&, U>::value &&
3801
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3802
  expected& operator=(U&& v) {
3803
    if (has_value()) {
3804
      val() = std::forward<U>(v);
3805
    } else {
3806
      err().~unexpected<E>();
3807
      ::new (valptr()) T(std::forward<U>(v));
3808
      this->m_has_val = true;
3809
    }
3810
3811
    return *this;
3812
  }
3813
3814
  template <
3815
      class U = T, class G = T,
3816
      detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* =
3817
          nullptr,
3818
      detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
3819
      detail::enable_if_t<
3820
          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
3821
           !detail::conjunction<std::is_scalar<T>,
3822
                                std::is_same<T, detail::decay_t<U>>>::value &&
3823
           std::is_constructible<T, U>::value &&
3824
           std::is_assignable<G&, U>::value &&
3825
           std::is_nothrow_move_constructible<E>::value)>* = nullptr>
3826
  expected& operator=(U&& v) {
3827
    if (has_value()) {
3828
      val() = std::forward<U>(v);
3829
    } else {
3830
      auto tmp = std::move(err());
3831
      err().~unexpected<E>();
3832
3833
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3834
      try {
3835
        ::new (valptr()) T(std::forward<U>(v));
3836
        this->m_has_val = true;
3837
      } catch (...) {
3838
        err() = std::move(tmp);
3839
        throw;
3840
      }
3841
#else
3842
      ::new (valptr()) T(std::forward<U>(v));
3843
      this->m_has_val = true;
3844
#endif
3845
    }
3846
3847
    return *this;
3848
  }
3849
3850
  template <class G = E,
3851
            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
3852
                                std::is_assignable<G&, G>::value>* = nullptr>
3853
  expected& operator=(const unexpected<G>& rhs) {
3854
    if (!has_value()) {
3855
      err() = rhs;
3856
    } else {
3857
      this->destroy_val();
3858
      ::new (errptr()) unexpected<E>(rhs);
3859
      this->m_has_val = false;
3860
    }
3861
3862
    return *this;
3863
  }
3864
3865
  template <class G = E,
3866
            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
3867
                                std::is_move_assignable<G>::value>* = nullptr>
3868
  expected& operator=(unexpected<G>&& rhs) noexcept {
3869
    if (!has_value()) {
3870
      err() = std::move(rhs);
3871
    } else {
3872
      this->destroy_val();
3873
      ::new (errptr()) unexpected<E>(std::move(rhs));
3874
      this->m_has_val = false;
3875
    }
3876
3877
    return *this;
3878
  }
3879
3880
  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
3881
                               T, Args&&...>::value>* = nullptr>
3882
  void emplace(Args&&... args) {
3883
    if (has_value()) {
3884
      val().~T();
3885
    } else {
3886
      err().~unexpected<E>();
3887
      this->m_has_val = true;
3888
    }
3889
    ::new (valptr()) T(std::forward<Args>(args)...);
3890
  }
3891
3892
  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
3893
                               T, Args&&...>::value>* = nullptr>
3894
  void emplace(Args&&... args) {
3895
    if (has_value()) {
3896
      val().~T();
3897
      ::new (valptr()) T(std::forward<Args>(args)...);
3898
    } else {
3899
      auto tmp = std::move(err());
3900
      err().~unexpected<E>();
3901
3902
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3903
      try {
3904
        ::new (valptr()) T(std::forward<Args>(args)...);
3905
        this->m_has_val = true;
3906
      } catch (...) {
3907
        err() = std::move(tmp);
3908
        throw;
3909
      }
3910
#else
3911
      ::new (valptr()) T(std::forward<Args>(args)...);
3912
      this->m_has_val = true;
3913
#endif
3914
    }
3915
  }
3916
3917
  template <class U, class... Args,
3918
            detail::enable_if_t<std::is_nothrow_constructible<
3919
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3920
  void emplace(std::initializer_list<U> il, Args&&... args) {
3921
    if (has_value()) {
3922
      T t(il, std::forward<Args>(args)...);
3923
      val() = std::move(t);
3924
    } else {
3925
      err().~unexpected<E>();
3926
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3927
      this->m_has_val = true;
3928
    }
3929
  }
3930
3931
  template <class U, class... Args,
3932
            detail::enable_if_t<!std::is_nothrow_constructible<
3933
                T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
3934
  void emplace(std::initializer_list<U> il, Args&&... args) {
3935
    if (has_value()) {
3936
      T t(il, std::forward<Args>(args)...);
3937
      val() = std::move(t);
3938
    } else {
3939
      auto tmp = std::move(err());
3940
      err().~unexpected<E>();
3941
3942
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
3943
      try {
3944
        ::new (valptr()) T(il, std::forward<Args>(args)...);
3945
        this->m_has_val = true;
3946
      } catch (...) {
3947
        err() = std::move(tmp);
3948
        throw;
3949
      }
3950
#else
3951
      ::new (valptr()) T(il, std::forward<Args>(args)...);
3952
      this->m_has_val = true;
3953
#endif
3954
    }
3955
  }
3956
3957
 private:
3958
  using t_is_void = std::true_type;
3959
  using t_is_not_void = std::false_type;
3960
  using t_is_nothrow_move_constructible = std::true_type;
3961
  using move_constructing_t_can_throw = std::false_type;
3962
  using e_is_nothrow_move_constructible = std::true_type;
3963
  using move_constructing_e_can_throw = std::false_type;
3964
3965
  void swap_where_both_have_value(expected& /*rhs*/, t_is_void) noexcept {
3966
    // swapping void is a no-op
3967
  }
3968
3969
  void swap_where_both_have_value(expected& rhs, t_is_not_void) {
3970
    using std::swap;
3971
    swap(val(), rhs.val());
3972
  }
3973
3974
  void swap_where_only_one_has_value(expected& rhs, t_is_void) noexcept(
3975
      std::is_nothrow_move_constructible<E>::value) {
3976
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3977
    rhs.err().~unexpected_type();
3978
    std::swap(this->m_has_val, rhs.m_has_val);
3979
  }
3980
3981
  void swap_where_only_one_has_value(expected& rhs, t_is_not_void) {
3982
    swap_where_only_one_has_value_and_t_is_not_void(
3983
        rhs, typename std::is_nothrow_move_constructible<T>::type{},
3984
        typename std::is_nothrow_move_constructible<E>::type{});
3985
  }
3986
3987
  void swap_where_only_one_has_value_and_t_is_not_void(
3988
      expected& rhs, t_is_nothrow_move_constructible,
3989
      e_is_nothrow_move_constructible) noexcept {
3990
    auto temp = std::move(val());
3991
    val().~T();
3992
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
3993
    rhs.err().~unexpected_type();
3994
    ::new (rhs.valptr()) T(std::move(temp));
3995
    std::swap(this->m_has_val, rhs.m_has_val);
3996
  }
3997
3998
  void swap_where_only_one_has_value_and_t_is_not_void(
3999
      expected& rhs, t_is_nothrow_move_constructible,
4000
      move_constructing_e_can_throw) {
4001
    auto temp = std::move(val());
4002
    val().~T();
4003
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4004
    try {
4005
      ::new (errptr()) unexpected_type(std::move(rhs.err()));
4006
      rhs.err().~unexpected_type();
4007
      ::new (rhs.valptr()) T(std::move(temp));
4008
      std::swap(this->m_has_val, rhs.m_has_val);
4009
    } catch (...) {
4010
      val() = std::move(temp);
4011
      throw;
4012
    }
4013
#else
4014
    ::new (errptr()) unexpected_type(std::move(rhs.err()));
4015
    rhs.err().~unexpected_type();
4016
    ::new (rhs.valptr()) T(std::move(temp));
4017
    std::swap(this->m_has_val, rhs.m_has_val);
4018
#endif
4019
  }
4020
4021
  void swap_where_only_one_has_value_and_t_is_not_void(
4022
      expected& rhs, move_constructing_t_can_throw,
4023
      e_is_nothrow_move_constructible) {
4024
    auto temp = std::move(rhs.err());
4025
    rhs.err().~unexpected_type();
4026
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
4027
    try {
4028
      ::new (rhs.valptr()) T(std::move(val()));
4029
      val().~T();
4030
      ::new (errptr()) unexpected_type(std::move(temp));
4031
      std::swap(this->m_has_val, rhs.m_has_val);
4032
    } catch (...) {
4033
      rhs.err() = std::move(temp);
4034
      throw;
4035
    }
4036
#else
4037
    ::new (rhs.valptr()) T(std::move(val()));
4038
    val().~T();
4039
    ::new (errptr()) unexpected_type(std::move(temp));
4040
    std::swap(this->m_has_val, rhs.m_has_val);
4041
#endif
4042
  }
4043
4044
 public:
4045
  template <class OT = T, class OE = E>
4046
  detail::enable_if_t<detail::is_swappable<OT>::value &&
4047
                      detail::is_swappable<OE>::value &&
4048
                      (std::is_nothrow_move_constructible<OT>::value ||
4049
                       std::is_nothrow_move_constructible<OE>::value)>
4050
  swap(expected& rhs) noexcept(std::is_nothrow_move_constructible<T>::value &&
4051
                               detail::is_nothrow_swappable<T>::value &&
4052
                               std::is_nothrow_move_constructible<E>::value &&
4053
                               detail::is_nothrow_swappable<E>::value) {
4054
    if (has_value() && rhs.has_value()) {
4055
      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
4056
    } else if (!has_value() && rhs.has_value()) {
4057
      rhs.swap(*this);
4058
    } else if (has_value()) {
4059
      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
4060
    } else {
4061
      using std::swap;
4062
      swap(err(), rhs.err());
4063
    }
4064
  }
4065
4066
  constexpr const T* operator->() const {
4067
    TL_ASSERT(has_value());
4068
    return valptr();
4069
  }
4070
11.1M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
11.1M
    TL_ASSERT(has_value());
4072
11.1M
    return valptr();
4073
11.1M
  }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator->()
tl::expected<ada::url_aggregator, ada::errors>::operator->()
Line
Count
Source
4070
2.57M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
2.57M
    TL_ASSERT(has_value());
4072
2.57M
    return valptr();
4073
2.57M
  }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::operator->()
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::operator->()
tl::expected<ada::url_pattern_init, ada::errors>::operator->()
Line
Count
Source
4070
5.31M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
5.31M
    TL_ASSERT(has_value());
4072
5.31M
    return valptr();
4073
5.31M
  }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::operator->()
Line
Count
Source
4070
777k
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
777k
    TL_ASSERT(has_value());
4072
777k
    return valptr();
4073
777k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator->()
Line
Count
Source
4070
2.47M
  TL_EXPECTED_11_CONSTEXPR T* operator->() {
4071
2.47M
    TL_ASSERT(has_value());
4072
2.47M
    return valptr();
4073
2.47M
  }
4074
4075
  template <class U = T,
4076
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4077
  constexpr const U& operator*() const& {
4078
    TL_ASSERT(has_value());
4079
    return val();
4080
  }
4081
  template <class U = T,
4082
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4083
9.19M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
9.19M
    TL_ASSERT(has_value());
4085
9.19M
    return val();
4086
9.19M
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4083
320k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
320k
    TL_ASSERT(has_value());
4085
320k
    return val();
4086
320k
  }
_ZNR2tl8expectedINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEN3ada6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
4083
2.16M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
2.16M
    TL_ASSERT(has_value());
4085
2.16M
    return val();
4086
2.16M
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada16url_pattern_partENS1_9allocatorIS4_EEEENS3_6errorsEEdeIS7_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSC_v
Line
Count
Source
4083
3.62M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
3.62M
    TL_ASSERT(has_value());
4085
3.62M
    return val();
4086
3.62M
  }
_ZNR2tl8expectedIN3ada11url_patternINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
4083
259k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
259k
    TL_ASSERT(has_value());
4085
259k
    return val();
4086
259k
  }
_ZNR2tl8expectedIbN3ada6errorsEEdeIbTnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS7_v
Line
Count
Source
4083
388k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
388k
    TL_ASSERT(has_value());
4085
388k
    return val();
4086
388k
  }
_ZNR2tl8expectedINSt3__18optionalIN3ada18url_pattern_resultEEENS3_6errorsEEdeIS5_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSA_v
Line
Count
Source
4083
46.4k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
46.4k
    TL_ASSERT(has_value());
4085
46.4k
    return val();
4086
46.4k
  }
_ZNR2tl8expectedINSt3__16vectorIN3ada19url_pattern_helpers5tokenENS1_9allocatorIS5_EEEENS3_6errorsEEdeIS8_TnPNS1_9enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSD_v
Line
Count
Source
4083
1.19M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
1.19M
    TL_ASSERT(has_value());
4085
1.19M
    return val();
4086
1.19M
  }
_ZNR2tl8expectedIN3ada16url_pattern_initENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v
Line
Count
Source
4083
88.6k
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
88.6k
    TL_ASSERT(has_value());
4085
88.6k
    return val();
4086
88.6k
  }
_ZNR2tl8expectedIN3ada21url_pattern_componentINS1_17url_pattern_regex18std_regex_providerEEENS1_6errorsEEdeIS5_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERSB_v
Line
Count
Source
4083
1.10M
  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
4084
1.10M
    TL_ASSERT(has_value());
4085
1.10M
    return val();
4086
1.10M
  }
Unexecuted instantiation: _ZNR2tl8expectedIN3ada23url_pattern_list_detail15compiled_routesENS1_6errorsEEdeIS3_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS9_v
4087
  template <class U = T,
4088
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4089
  constexpr const U&& operator*() const&& {
4090
    TL_ASSERT(has_value());
4091
    return std::move(val());
4092
  }
4093
  template <class U = T,
4094
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4095
  TL_EXPECTED_11_CONSTEXPR U&& operator*() && {
4096
    TL_ASSERT(has_value());
4097
    return std::move(val());
4098
  }
4099
4100
21.2M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::has_value() const
tl::expected<ada::url_aggregator, ada::errors>::has_value() const
Line
Count
Source
4100
3.54M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::has_value() const
Line
Count
Source
4100
2.17M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::has_value() const
Line
Count
Source
4100
6.10M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)0>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::basic_string_view<char, std::__1::char_traits<char> >, (ada::url_search_params_iter_type)1>, ada::errors>::has_value() const
Unexecuted instantiation: tl::expected<ada::url_search_params_iter<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >, (ada::url_search_params_iter_type)2>, ada::errors>::has_value() const
tl::expected<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors>::has_value() const
Line
Count
Source
4100
259k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_init, ada::errors>::has_value() const
Line
Count
Source
4100
5.59M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::has_value() const
Line
Count
Source
4100
824k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<bool, ada::errors>::has_value() const
Line
Count
Source
4100
388k
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::has_value() const
Line
Count
Source
4100
1.20M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::has_value() const
Line
Count
Source
4100
1.11M
  constexpr bool has_value() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::has_value() const
4101
8.59M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url, ada::errors>::operator bool() const
tl::expected<ada::url_aggregator, ada::errors>::operator bool() const
Line
Count
Source
4101
1.18M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::operator bool() const
Line
Count
Source
4101
2.17M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::operator bool() const
Line
Count
Source
4101
1.11M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_search_params, ada::errors>::operator bool() const
tl::expected<ada::url_pattern<ada::url_pattern_regex::std_regex_provider>, ada::errors>::operator bool() const
Line
Count
Source
4101
240k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_init, ada::errors>::operator bool() const
Line
Count
Source
4101
413k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::optional<ada::url_pattern_result>, ada::errors>::operator bool() const
Line
Count
Source
4101
777k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<bool, ada::errors>::operator bool() const
Line
Count
Source
4101
388k
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::operator bool() const
Line
Count
Source
4101
1.20M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::operator bool() const
Line
Count
Source
4101
1.11M
  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::operator bool() const
4102
4103
  template <class U = T,
4104
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4105
  TL_EXPECTED_11_CONSTEXPR const U& value() const& {
4106
    if (!has_value())
4107
      detail::throw_exception(bad_expected_access<E>(err().value()));
4108
    return val();
4109
  }
4110
  template <class U = T,
4111
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4112
0
  TL_EXPECTED_11_CONSTEXPR U& value() & {
4113
0
    if (!has_value())
4114
0
      detail::throw_exception(bad_expected_access<E>(err().value()));
4115
0
    return val();
4116
0
  }
4117
  template <class U = T,
4118
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4119
  TL_EXPECTED_11_CONSTEXPR const U&& value() const&& {
4120
    if (!has_value())
4121
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4122
    return std::move(val());
4123
  }
4124
  template <class U = T,
4125
            detail::enable_if_t<!std::is_void<U>::value>* = nullptr>
4126
  TL_EXPECTED_11_CONSTEXPR U&& value() && {
4127
    if (!has_value())
4128
      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
4129
    return std::move(val());
4130
  }
4131
4132
  constexpr const E& error() const& {
4133
    TL_ASSERT(!has_value());
4134
    return err().value();
4135
  }
4136
80.9k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
80.9k
    TL_ASSERT(!has_value());
4138
80.9k
    return err().value();
4139
80.9k
  }
tl::expected<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ada::errors>::error() &
Line
Count
Source
4136
5.04k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
5.04k
    TL_ASSERT(!has_value());
4138
5.04k
    return err().value();
4139
5.04k
  }
tl::expected<std::__1::vector<ada::url_pattern_part, std::__1::allocator<ada::url_pattern_part> >, ada::errors>::error() &
Line
Count
Source
4136
7.11k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
7.11k
    TL_ASSERT(!has_value());
4138
7.11k
    return err().value();
4139
7.11k
  }
tl::expected<std::__1::vector<ada::url_pattern_helpers::token, std::__1::allocator<ada::url_pattern_helpers::token> >, ada::errors>::error() &
Line
Count
Source
4136
1.50k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
1.50k
    TL_ASSERT(!has_value());
4138
1.50k
    return err().value();
4139
1.50k
  }
tl::expected<ada::url_pattern_init, ada::errors>::error() &
Line
Count
Source
4136
59.0k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
59.0k
    TL_ASSERT(!has_value());
4138
59.0k
    return err().value();
4139
59.0k
  }
tl::expected<ada::url_pattern_component<ada::url_pattern_regex::std_regex_provider>, ada::errors>::error() &
Line
Count
Source
4136
8.28k
  TL_EXPECTED_11_CONSTEXPR E& error() & {
4137
8.28k
    TL_ASSERT(!has_value());
4138
8.28k
    return err().value();
4139
8.28k
  }
Unexecuted instantiation: tl::expected<ada::url_pattern_list_detail::compiled_routes, ada::errors>::error() &
4140
  constexpr const E&& error() const&& {
4141
    TL_ASSERT(!has_value());
4142
    return std::move(err().value());
4143
  }
4144
  TL_EXPECTED_11_CONSTEXPR E&& error() && {
4145
    TL_ASSERT(!has_value());
4146
    return std::move(err().value());
4147
  }
4148
4149
  template <class U>
4150
  constexpr T value_or(U&& v) const& {
4151
    static_assert(std::is_copy_constructible<T>::value &&
4152
                      std::is_convertible<U&&, T>::value,
4153
                  "T must be copy-constructible and convertible to from U&&");
4154
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
4155
  }
4156
  template <class U>
4157
  TL_EXPECTED_11_CONSTEXPR T value_or(U&& v) && {
4158
    static_assert(std::is_move_constructible<T>::value &&
4159
                      std::is_convertible<U&&, T>::value,
4160
                  "T must be move-constructible and convertible to from U&&");
4161
    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
4162
  }
4163
};
4164
4165
namespace detail {
4166
template <class Exp>
4167
using exp_t = typename detail::decay_t<Exp>::value_type;
4168
template <class Exp>
4169
using err_t = typename detail::decay_t<Exp>::error_type;
4170
template <class Exp, class Ret>
4171
using ret_t = expected<Ret, err_t<Exp>>;
4172
4173
#ifdef TL_EXPECTED_CXX14
4174
template <class Exp, class F,
4175
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4176
          class Ret = decltype(detail::invoke(std::declval<F>(),
4177
                                              *std::declval<Exp>()))>
4178
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4179
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4180
4181
  return exp.has_value()
4182
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4183
             : Ret(unexpect, std::forward<Exp>(exp).error());
4184
}
4185
4186
template <class Exp, class F,
4187
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4188
          class Ret = decltype(detail::invoke(std::declval<F>()))>
4189
constexpr auto and_then_impl(Exp&& exp, F&& f) {
4190
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4191
4192
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4193
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4194
}
4195
#else
4196
template <class>
4197
struct TC;
4198
template <class Exp, class F,
4199
          class Ret = decltype(detail::invoke(std::declval<F>(),
4200
                                              *std::declval<Exp>())),
4201
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr>
4202
auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4203
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4204
4205
  return exp.has_value()
4206
             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
4207
             : Ret(unexpect, std::forward<Exp>(exp).error());
4208
}
4209
4210
template <class Exp, class F,
4211
          class Ret = decltype(detail::invoke(std::declval<F>())),
4212
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr>
4213
constexpr auto and_then_impl(Exp&& exp, F&& f) -> Ret {
4214
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4215
4216
  return exp.has_value() ? detail::invoke(std::forward<F>(f))
4217
                         : Ret(unexpect, std::forward<Exp>(exp).error());
4218
}
4219
#endif
4220
4221
#ifdef TL_EXPECTED_CXX14
4222
template <class Exp, class F,
4223
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4224
          class Ret = decltype(detail::invoke(std::declval<F>(),
4225
                                              *std::declval<Exp>())),
4226
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4227
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4228
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4229
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4230
                                                 *std::forward<Exp>(exp)))
4231
                         : result(unexpect, std::forward<Exp>(exp).error());
4232
}
4233
4234
template <class Exp, class F,
4235
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4236
          class Ret = decltype(detail::invoke(std::declval<F>(),
4237
                                              *std::declval<Exp>())),
4238
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4239
auto expected_map_impl(Exp&& exp, F&& f) {
4240
  using result = expected<void, err_t<Exp>>;
4241
  if (exp.has_value()) {
4242
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4243
    return result();
4244
  }
4245
4246
  return result(unexpect, std::forward<Exp>(exp).error());
4247
}
4248
4249
template <class Exp, class F,
4250
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4251
          class Ret = decltype(detail::invoke(std::declval<F>())),
4252
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4253
constexpr auto expected_map_impl(Exp&& exp, F&& f) {
4254
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4255
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4256
                         : result(unexpect, std::forward<Exp>(exp).error());
4257
}
4258
4259
template <class Exp, class F,
4260
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4261
          class Ret = decltype(detail::invoke(std::declval<F>())),
4262
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4263
auto expected_map_impl(Exp&& exp, F&& f) {
4264
  using result = expected<void, err_t<Exp>>;
4265
  if (exp.has_value()) {
4266
    detail::invoke(std::forward<F>(f));
4267
    return result();
4268
  }
4269
4270
  return result(unexpect, std::forward<Exp>(exp).error());
4271
}
4272
#else
4273
template <class Exp, class F,
4274
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4275
          class Ret = decltype(detail::invoke(std::declval<F>(),
4276
                                              *std::declval<Exp>())),
4277
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4278
4279
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4280
    -> ret_t<Exp, detail::decay_t<Ret>> {
4281
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4282
4283
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
4284
                                                 *std::forward<Exp>(exp)))
4285
                         : result(unexpect, std::forward<Exp>(exp).error());
4286
}
4287
4288
template <class Exp, class F,
4289
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4290
          class Ret = decltype(detail::invoke(std::declval<F>(),
4291
                                              *std::declval<Exp>())),
4292
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4293
4294
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4295
  if (exp.has_value()) {
4296
    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
4297
    return {};
4298
  }
4299
4300
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4301
}
4302
4303
template <class Exp, class F,
4304
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4305
          class Ret = decltype(detail::invoke(std::declval<F>())),
4306
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4307
4308
constexpr auto expected_map_impl(Exp&& exp, F&& f)
4309
    -> ret_t<Exp, detail::decay_t<Ret>> {
4310
  using result = ret_t<Exp, detail::decay_t<Ret>>;
4311
4312
  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
4313
                         : result(unexpect, std::forward<Exp>(exp).error());
4314
}
4315
4316
template <class Exp, class F,
4317
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4318
          class Ret = decltype(detail::invoke(std::declval<F>())),
4319
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4320
4321
auto expected_map_impl(Exp&& exp, F&& f) -> expected<void, err_t<Exp>> {
4322
  if (exp.has_value()) {
4323
    detail::invoke(std::forward<F>(f));
4324
    return {};
4325
  }
4326
4327
  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
4328
}
4329
#endif
4330
4331
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
4332
    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
4333
template <class Exp, class F,
4334
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4335
          class Ret = decltype(detail::invoke(std::declval<F>(),
4336
                                              std::declval<Exp>().error())),
4337
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4338
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4339
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4340
  return exp.has_value()
4341
             ? result(*std::forward<Exp>(exp))
4342
             : result(unexpect, detail::invoke(std::forward<F>(f),
4343
                                               std::forward<Exp>(exp).error()));
4344
}
4345
template <class Exp, class F,
4346
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4347
          class Ret = decltype(detail::invoke(std::declval<F>(),
4348
                                              std::declval<Exp>().error())),
4349
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4350
auto map_error_impl(Exp&& exp, F&& f) {
4351
  using result = expected<exp_t<Exp>, monostate>;
4352
  if (exp.has_value()) {
4353
    return result(*std::forward<Exp>(exp));
4354
  }
4355
4356
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4357
  return result(unexpect, monostate{});
4358
}
4359
template <class Exp, class F,
4360
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4361
          class Ret = decltype(detail::invoke(std::declval<F>(),
4362
                                              std::declval<Exp>().error())),
4363
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4364
constexpr auto map_error_impl(Exp&& exp, F&& f) {
4365
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4366
  return exp.has_value()
4367
             ? result()
4368
             : result(unexpect, detail::invoke(std::forward<F>(f),
4369
                                               std::forward<Exp>(exp).error()));
4370
}
4371
template <class Exp, class F,
4372
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4373
          class Ret = decltype(detail::invoke(std::declval<F>(),
4374
                                              std::declval<Exp>().error())),
4375
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4376
auto map_error_impl(Exp&& exp, F&& f) {
4377
  using result = expected<exp_t<Exp>, monostate>;
4378
  if (exp.has_value()) {
4379
    return result();
4380
  }
4381
4382
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4383
  return result(unexpect, monostate{});
4384
}
4385
#else
4386
template <class Exp, class F,
4387
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4388
          class Ret = decltype(detail::invoke(std::declval<F>(),
4389
                                              std::declval<Exp>().error())),
4390
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4391
constexpr auto map_error_impl(Exp&& exp, F&& f)
4392
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4393
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4394
4395
  return exp.has_value()
4396
             ? result(*std::forward<Exp>(exp))
4397
             : result(unexpect, detail::invoke(std::forward<F>(f),
4398
                                               std::forward<Exp>(exp).error()));
4399
}
4400
4401
template <class Exp, class F,
4402
          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value>* = nullptr,
4403
          class Ret = decltype(detail::invoke(std::declval<F>(),
4404
                                              std::declval<Exp>().error())),
4405
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4406
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4407
  using result = expected<exp_t<Exp>, monostate>;
4408
  if (exp.has_value()) {
4409
    return result(*std::forward<Exp>(exp));
4410
  }
4411
4412
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4413
  return result(unexpect, monostate{});
4414
}
4415
4416
template <class Exp, class F,
4417
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4418
          class Ret = decltype(detail::invoke(std::declval<F>(),
4419
                                              std::declval<Exp>().error())),
4420
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4421
constexpr auto map_error_impl(Exp&& exp, F&& f)
4422
    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
4423
  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
4424
4425
  return exp.has_value()
4426
             ? result()
4427
             : result(unexpect, detail::invoke(std::forward<F>(f),
4428
                                               std::forward<Exp>(exp).error()));
4429
}
4430
4431
template <class Exp, class F,
4432
          detail::enable_if_t<std::is_void<exp_t<Exp>>::value>* = nullptr,
4433
          class Ret = decltype(detail::invoke(std::declval<F>(),
4434
                                              std::declval<Exp>().error())),
4435
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4436
auto map_error_impl(Exp&& exp, F&& f) -> expected<exp_t<Exp>, monostate> {
4437
  using result = expected<exp_t<Exp>, monostate>;
4438
  if (exp.has_value()) {
4439
    return result();
4440
  }
4441
4442
  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
4443
  return result(unexpect, monostate{});
4444
}
4445
#endif
4446
4447
#ifdef TL_EXPECTED_CXX14
4448
template <class Exp, class F,
4449
          class Ret = decltype(detail::invoke(std::declval<F>(),
4450
                                              std::declval<Exp>().error())),
4451
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4452
constexpr auto or_else_impl(Exp&& exp, F&& f) {
4453
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4454
  return exp.has_value() ? std::forward<Exp>(exp)
4455
                         : detail::invoke(std::forward<F>(f),
4456
                                          std::forward<Exp>(exp).error());
4457
}
4458
4459
template <class Exp, class F,
4460
          class Ret = decltype(detail::invoke(std::declval<F>(),
4461
                                              std::declval<Exp>().error())),
4462
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4463
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4464
  return exp.has_value() ? std::forward<Exp>(exp)
4465
                         : (detail::invoke(std::forward<F>(f),
4466
                                           std::forward<Exp>(exp).error()),
4467
                            std::forward<Exp>(exp));
4468
}
4469
#else
4470
template <class Exp, class F,
4471
          class Ret = decltype(detail::invoke(std::declval<F>(),
4472
                                              std::declval<Exp>().error())),
4473
          detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
4474
auto or_else_impl(Exp&& exp, F&& f) -> Ret {
4475
  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
4476
  return exp.has_value() ? std::forward<Exp>(exp)
4477
                         : detail::invoke(std::forward<F>(f),
4478
                                          std::forward<Exp>(exp).error());
4479
}
4480
4481
template <class Exp, class F,
4482
          class Ret = decltype(detail::invoke(std::declval<F>(),
4483
                                              std::declval<Exp>().error())),
4484
          detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
4485
detail::decay_t<Exp> or_else_impl(Exp&& exp, F&& f) {
4486
  return exp.has_value() ? std::forward<Exp>(exp)
4487
                         : (detail::invoke(std::forward<F>(f),
4488
                                           std::forward<Exp>(exp).error()),
4489
                            std::forward<Exp>(exp));
4490
}
4491
#endif
4492
}  // namespace detail
4493
4494
template <class T, class E, class U, class F>
4495
constexpr bool operator==(const expected<T, E>& lhs,
4496
                          const expected<U, F>& rhs) {
4497
  return (lhs.has_value() != rhs.has_value())
4498
             ? false
4499
             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
4500
}
4501
template <class T, class E, class U, class F>
4502
constexpr bool operator!=(const expected<T, E>& lhs,
4503
                          const expected<U, F>& rhs) {
4504
  return (lhs.has_value() != rhs.has_value())
4505
             ? true
4506
             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
4507
}
4508
template <class E, class F>
4509
constexpr bool operator==(const expected<void, E>& lhs,
4510
                          const expected<void, F>& rhs) {
4511
  return (lhs.has_value() != rhs.has_value())
4512
             ? false
4513
             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
4514
}
4515
template <class E, class F>
4516
constexpr bool operator!=(const expected<void, E>& lhs,
4517
                          const expected<void, F>& rhs) {
4518
  return (lhs.has_value() != rhs.has_value())
4519
             ? true
4520
             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
4521
}
4522
4523
template <class T, class E, class U>
4524
constexpr bool operator==(const expected<T, E>& x, const U& v) {
4525
  return x.has_value() ? *x == v : false;
4526
}
4527
template <class T, class E, class U>
4528
constexpr bool operator==(const U& v, const expected<T, E>& x) {
4529
  return x.has_value() ? *x == v : false;
4530
}
4531
template <class T, class E, class U>
4532
constexpr bool operator!=(const expected<T, E>& x, const U& v) {
4533
  return x.has_value() ? *x != v : true;
4534
}
4535
template <class T, class E, class U>
4536
constexpr bool operator!=(const U& v, const expected<T, E>& x) {
4537
  return x.has_value() ? *x != v : true;
4538
}
4539
4540
template <class T, class E>
4541
constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& e) {
4542
  return x.has_value() ? false : x.error() == e.value();
4543
}
4544
template <class T, class E>
4545
constexpr bool operator==(const unexpected<E>& e, const expected<T, E>& x) {
4546
  return x.has_value() ? false : x.error() == e.value();
4547
}
4548
template <class T, class E>
4549
constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& e) {
4550
  return x.has_value() ? true : x.error() != e.value();
4551
}
4552
template <class T, class E>
4553
constexpr bool operator!=(const unexpected<E>& e, const expected<T, E>& x) {
4554
  return x.has_value() ? true : x.error() != e.value();
4555
}
4556
4557
template <class T, class E,
4558
          detail::enable_if_t<(std::is_void<T>::value ||
4559
                               std::is_move_constructible<T>::value) &&
4560
                              detail::is_swappable<T>::value &&
4561
                              std::is_move_constructible<E>::value &&
4562
                              detail::is_swappable<E>::value>* = nullptr>
4563
void swap(expected<T, E>& lhs,
4564
          expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
4565
  lhs.swap(rhs);
4566
}
4567
}  // namespace tl
4568
4569
#endif
4570
/* end file include/ada/expected.h */
4571
4572
/* begin file include/ada/url_pattern_regex.h */
4573
/**
4574
 * @file url_search_params.h
4575
 * @brief Declaration for the URL Search Params
4576
 */
4577
#ifndef ADA_URL_PATTERN_REGEX_H
4578
#define ADA_URL_PATTERN_REGEX_H
4579
4580
#include <concepts>
4581
#include <optional>
4582
#include <string>
4583
#include <string_view>
4584
#include <vector>
4585
4586
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4587
#include <regex>
4588
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4589
4590
#if ADA_INCLUDE_URL_PATTERN
4591
namespace ada::url_pattern_regex {
4592
4593
/**
4594
 * @brief Concept defining the interface for regex engine providers used by
4595
 * URLPattern.
4596
 *
4597
 * A regex provider supplies the regular expression engine used for
4598
 * pattern compilation and matching.
4599
 *
4600
 * Requirements:
4601
 * - typename T::regex_type: The compiled regex object type.
4602
 * - T::create_instance(pattern, ignore_case): Compiles the regex pattern
4603
 * string. Returns std::nullopt if the pattern is invalid or cannot be compiled.
4604
 * - T::regex_search(input, pattern): Executes a regex search against the input,
4605
 *     returning a vector of capture group values (with std::nullopt for
4606
 * unmatched optional groups) or std::nullopt if the input does not match.
4607
 * - T::regex_match(input, pattern): Returns true if the input matches the
4608
 * pattern, or false otherwise.
4609
 *
4610
 * Exception contract:
4611
 * Provider methods may throw exceptions (for example, on allocation failure,
4612
 * input length limits, complexity limits, or internal engine errors). In
4613
 * environments where C++ exceptions are enabled, exceptions thrown by the
4614
 * provider will propagate out of URLPattern functions (such as fast_test,
4615
 * fast_match, test, exec, match, etc.). Providers that prefer non-throwing
4616
 * operation should handle internal errors and return false or std::nullopt.
4617
 */
4618
template <typename T>
4619
concept regex_concept = requires(T t, std::string_view pattern,
4620
                                 bool ignore_case, std::string_view input) {
4621
  // Ensure the class has a type alias 'regex_type'
4622
  typename T::regex_type;
4623
4624
  // Function to create a regex instance
4625
  {
4626
    T::create_instance(pattern, ignore_case)
4627
  } -> std::same_as<std::optional<typename T::regex_type>>;
4628
4629
  // Function to perform regex search
4630
  {
4631
    T::regex_search(input, std::declval<typename T::regex_type&>())
4632
  } -> std::same_as<std::optional<std::vector<std::optional<std::string>>>>;
4633
4634
  // Function to match regex pattern
4635
  {
4636
    T::regex_match(input, std::declval<typename T::regex_type&>())
4637
  } -> std::same_as<bool>;
4638
4639
  // Copy constructor
4640
  { T(std::declval<const T&>()) } -> std::same_as<T>;
4641
4642
  // Move constructor
4643
  { T(std::declval<T&&>()) } -> std::same_as<T>;
4644
};
4645
4646
#ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4647
class std_regex_provider final {
4648
 public:
4649
  std_regex_provider() = default;
4650
  using regex_type = std::regex;
4651
  static std::optional<regex_type> create_instance(std::string_view pattern,
4652
                                                   bool ignore_case);
4653
  static std::optional<std::vector<std::optional<std::string>>> regex_search(
4654
      std::string_view input, const regex_type& pattern);
4655
  static bool regex_match(std::string_view input, const regex_type& pattern);
4656
};
4657
#endif  // ADA_USE_UNSAFE_STD_REGEX_PROVIDER
4658
4659
}  // namespace ada::url_pattern_regex
4660
#endif  // ADA_INCLUDE_URL_PATTERN
4661
#endif  // ADA_URL_PATTERN_REGEX_H
4662
/* end file include/ada/url_pattern_regex.h */
4663
/* begin file include/ada/url_pattern_init.h */
4664
/**
4665
 * @file url_pattern_init.h
4666
 * @brief Declaration for the url_pattern_init implementation.
4667
 */
4668
#ifndef ADA_URL_PATTERN_INIT_H
4669
#define ADA_URL_PATTERN_INIT_H
4670
4671
/* begin file include/ada/errors.h */
4672
/**
4673
 * @file errors.h
4674
 * @brief Error type definitions for URL parsing.
4675
 *
4676
 * Defines the error codes that can be returned when URL parsing fails.
4677
 */
4678
#ifndef ADA_ERRORS_H
4679
#define ADA_ERRORS_H
4680
4681
#include <cstdint>
4682
namespace ada {
4683
/**
4684
 * @brief Error codes for URL parsing operations.
4685
 *
4686
 * Used with `tl::expected` to indicate why a URL parsing operation failed.
4687
 */
4688
enum class errors : uint8_t {
4689
  type_error /**< A type error occurred (e.g., invalid URL syntax). */
4690
};
4691
}  // namespace ada
4692
#endif  // ADA_ERRORS_H
4693
/* end file include/ada/errors.h */
4694
4695
#include <concepts>
4696
#include <cstdint>
4697
#include <iostream>
4698
#include <optional>
4699
#include <string>
4700
#include <string_view>
4701
4702
#if ADA_TESTING
4703
#include <iostream>
4704
#endif  // ADA_TESTING
4705
4706
#if ADA_INCLUDE_URL_PATTERN
4707
namespace ada {
4708
4709
// Important: C++20 allows us to use concept rather than `using` or `typedef
4710
// and allows functions with second argument, which is optional (using either
4711
// std::nullopt or a parameter with default value)
4712
template <typename F>
4713
concept url_pattern_encoding_callback = requires(F f, std::string_view sv) {
4714
  { f(sv) } -> std::same_as<tl::expected<std::string, errors>>;
4715
};
4716
4717
// A structure providing matching patterns for individual components
4718
// of a URL. When a URLPattern is created, or when a URLPattern is
4719
// used to match or test against a URL, the input can be given as
4720
// either a string or a URLPatternInit struct. If a string is given,
4721
// it will be parsed to create a URLPatternInit. The URLPatternInit
4722
// API is defined as part of the URLPattern specification.
4723
// All provided strings must be valid UTF-8.
4724
struct url_pattern_init {
4725
  enum class process_type : uint8_t {
4726
    url,
4727
    pattern,
4728
  };
4729
4730
0
  friend std::ostream& operator<<(std::ostream& os, process_type type) {
4731
0
    switch (type) {
4732
0
      case process_type::url:
4733
0
        return os << "url";
4734
0
      case process_type::pattern:
4735
0
        return os << "pattern";
4736
0
      default:
4737
0
        return os << "unknown";
4738
0
    }
4739
0
  }
4740
4741
  // All strings must be valid UTF-8.
4742
  // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
4743
  static tl::expected<url_pattern_init, errors> process(
4744
      const url_pattern_init& init, process_type type,
4745
      std::optional<std::string_view> protocol = std::nullopt,
4746
      std::optional<std::string_view> username = std::nullopt,
4747
      std::optional<std::string_view> password = std::nullopt,
4748
      std::optional<std::string_view> hostname = std::nullopt,
4749
      std::optional<std::string_view> port = std::nullopt,
4750
      std::optional<std::string_view> pathname = std::nullopt,
4751
      std::optional<std::string_view> search = std::nullopt,
4752
      std::optional<std::string_view> hash = std::nullopt);
4753
4754
  // @see https://urlpattern.spec.whatwg.org/#process-protocol-for-init
4755
  static tl::expected<std::string, errors> process_protocol(
4756
      std::string_view value, process_type type);
4757
4758
  // @see https://urlpattern.spec.whatwg.org/#process-username-for-init
4759
  static tl::expected<std::string, errors> process_username(
4760
      std::string_view value, process_type type);
4761
4762
  // @see https://urlpattern.spec.whatwg.org/#process-password-for-init
4763
  static tl::expected<std::string, errors> process_password(
4764
      std::string_view value, process_type type);
4765
4766
  // @see https://urlpattern.spec.whatwg.org/#process-hostname-for-init
4767
  static tl::expected<std::string, errors> process_hostname(
4768
      std::string_view value, process_type type);
4769
4770
  // @see https://urlpattern.spec.whatwg.org/#process-port-for-init
4771
  static tl::expected<std::string, errors> process_port(
4772
      std::string_view port, std::string_view protocol, process_type type);
4773
4774
  // @see https://urlpattern.spec.whatwg.org/#process-pathname-for-init
4775
  static tl::expected<std::string, errors> process_pathname(
4776
      std::string_view value, std::string_view protocol, process_type type);
4777
4778
  // @see https://urlpattern.spec.whatwg.org/#process-search-for-init
4779
  static tl::expected<std::string, errors> process_search(
4780
      std::string_view value, process_type type);
4781
4782
  // @see https://urlpattern.spec.whatwg.org/#process-hash-for-init
4783
  static tl::expected<std::string, errors> process_hash(std::string_view value,
4784
                                                        process_type type);
4785
4786
#if ADA_TESTING
4787
  friend void PrintTo(const url_pattern_init& init, std::ostream* os) {
4788
    *os << "protocol: '" << init.protocol.value_or("undefined") << "', ";
4789
    *os << "username: '" << init.username.value_or("undefined") << "', ";
4790
    *os << "password: '" << init.password.value_or("undefined") << "', ";
4791
    *os << "hostname: '" << init.hostname.value_or("undefined") << "', ";
4792
    *os << "port: '" << init.port.value_or("undefined") << "', ";
4793
    *os << "pathname: '" << init.pathname.value_or("undefined") << "', ";
4794
    *os << "search: '" << init.search.value_or("undefined") << "', ";
4795
    *os << "hash: '" << init.hash.value_or("undefined") << "', ";
4796
    *os << "base_url: '" << init.base_url.value_or("undefined") << "', ";
4797
  }
4798
#endif  // ADA_TESTING
4799
4800
  bool operator==(const url_pattern_init&) const;
4801
  // If present, must be valid UTF-8.
4802
  std::optional<std::string> protocol{};
4803
  // If present, must be valid UTF-8.
4804
  std::optional<std::string> username{};
4805
  // If present, must be valid UTF-8.
4806
  std::optional<std::string> password{};
4807
  // If present, must be valid UTF-8.
4808
  std::optional<std::string> hostname{};
4809
  // If present, must be valid UTF-8.
4810
  std::optional<std::string> port{};
4811
  // If present, must be valid UTF-8.
4812
  std::optional<std::string> pathname{};
4813
  // If present, must be valid UTF-8.
4814
  std::optional<std::string> search{};
4815
  // If present, must be valid UTF-8.
4816
  std::optional<std::string> hash{};
4817
  // If present, must be valid UTF-8.
4818
  std::optional<std::string> base_url{};
4819
};
4820
}  // namespace ada
4821
#endif  // ADA_INCLUDE_URL_PATTERN
4822
#endif  // ADA_URL_PATTERN_INIT_H
4823
/* end file include/ada/url_pattern_init.h */
4824
4825
/** @private Forward declarations */
4826
namespace ada {
4827
struct url_aggregator;
4828
struct url;
4829
#if ADA_INCLUDE_URL_PATTERN
4830
template <url_pattern_regex::regex_concept regex_provider>
4831
class url_pattern;
4832
struct url_pattern_options;
4833
#endif  // ADA_INCLUDE_URL_PATTERN
4834
enum class errors : uint8_t;
4835
}  // namespace ada
4836
4837
/**
4838
 * @namespace ada::parser
4839
 * @brief Internal URL parsing implementation.
4840
 *
4841
 * Contains the core URL parsing algorithm as specified by the WHATWG URL
4842
 * Standard. These functions are used internally by `ada::parse()`.
4843
 */
4844
namespace ada::parser {
4845
/**
4846
 * Parses a URL string into a URL object.
4847
 *
4848
 * @tparam result_type The type of URL object to create (url or url_aggregator).
4849
 *
4850
 * @param user_input The URL string to parse (must be valid UTF-8).
4851
 * @param base_url Optional base URL for resolving relative URLs.
4852
 *
4853
 * @return The parsed URL object. Check `is_valid` to determine if parsing
4854
 *         succeeded.
4855
 *
4856
 * @see https://url.spec.whatwg.org/#concept-basic-url-parser
4857
 */
4858
template <typename result_type = url_aggregator>
4859
result_type parse_url(std::string_view user_input,
4860
                      const result_type* base_url = nullptr);
4861
4862
extern template url_aggregator parse_url<url_aggregator>(
4863
    std::string_view user_input, const url_aggregator* base_url);
4864
extern template url parse_url<url>(std::string_view user_input,
4865
                                   const url* base_url);
4866
4867
template <typename result_type = url_aggregator, bool store_values = true>
4868
result_type parse_url_impl(std::string_view user_input,
4869
                           const result_type* base_url = nullptr);
4870
4871
extern template url_aggregator parse_url_impl<url_aggregator, true>(
4872
    std::string_view user_input, const url_aggregator* base_url);
4873
extern template url_aggregator parse_url_impl<url_aggregator, false>(
4874
    std::string_view user_input, const url_aggregator* base_url);
4875
extern template url parse_url_impl<url, true>(std::string_view user_input,
4876
                                              const url* base_url);
4877
4878
/** @private */
4879
template <class result_type>
4880
bool try_parse_simple_absolute(std::string_view input, result_type& out);
4881
4882
/** @private */
4883
template <class result_type>
4884
bool finish_simple_absolute_with_port(std::string_view input, result_type& out,
4885
                                      ada::scheme::type scheme_type,
4886
                                      uint32_t protocol_end, size_t host_start,
4887
                                      size_t host_end, size_t host_len,
4888
                                      bool has_upper);
4889
4890
/** @private */
4891
template <class result_type>
4892
bool try_parse_simple_relative(std::string_view input, const result_type& base,
4893
                               result_type& out);
4894
4895
#if ADA_INCLUDE_URL_PATTERN
4896
template <url_pattern_regex::regex_concept regex_provider>
4897
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
4898
    std::variant<std::string_view, url_pattern_init>&& input,
4899
    const std::string_view* base_url, const url_pattern_options* options);
4900
#endif  // ADA_INCLUDE_URL_PATTERN
4901
4902
}  // namespace ada::parser
4903
4904
#endif  // ADA_PARSER_H
4905
/* end file include/ada/parser.h */
4906
/* begin file include/ada/parser-inl.h */
4907
/**
4908
 * @file parser-inl.h
4909
 */
4910
#ifndef ADA_PARSER_INL_H
4911
#define ADA_PARSER_INL_H
4912
4913
/* begin file include/ada/url_pattern.h */
4914
/**
4915
 * @file url_pattern.h
4916
 * @brief URLPattern API implementation.
4917
 *
4918
 * This header provides the URLPattern API as specified by the WHATWG URL
4919
 * Pattern Standard. URLPattern allows matching URLs against patterns with
4920
 * wildcards and named groups, similar to how regular expressions match strings.
4921
 *
4922
 * @see https://urlpattern.spec.whatwg.org/
4923
 * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
4924
 */
4925
#ifndef ADA_URL_PATTERN_H
4926
#define ADA_URL_PATTERN_H
4927
4928
/* begin file include/ada/implementation.h */
4929
/**
4930
 * @file implementation.h
4931
 * @brief User-facing functions for URL parsing and manipulation.
4932
 *
4933
 * This header provides the primary public API for parsing URLs in Ada.
4934
 * It includes the main `ada::parse()` function which is the recommended
4935
 * entry point for most users.
4936
 *
4937
 * @see https://url.spec.whatwg.org/#api
4938
 */
4939
#ifndef ADA_IMPLEMENTATION_H
4940
#define ADA_IMPLEMENTATION_H
4941
4942
#include <cstdint>
4943
#include <optional>
4944
#include <span>
4945
#include <string>
4946
#include <string_view>
4947
#include <variant>
4948
4949
/* begin file include/ada/url.h */
4950
/**
4951
 * @file url.h
4952
 * @brief Declaration for the `ada::url` class.
4953
 *
4954
 * This file contains the `ada::url` struct which represents a parsed URL
4955
 * using separate `std::string` instances for each component. This
4956
 * representation is more flexible but uses more memory than `url_aggregator`.
4957
 *
4958
 * @see url_aggregator.h for a more memory-efficient alternative
4959
 */
4960
#ifndef ADA_URL_H
4961
#define ADA_URL_H
4962
4963
#include <algorithm>
4964
#include <cstddef>
4965
#include <cstdint>
4966
#include <optional>
4967
#include <ostream>
4968
#include <string>
4969
#include <string_view>
4970
4971
/* begin file include/ada/url_components.h */
4972
/**
4973
 * @file url_components.h
4974
 * @brief URL component offset representation for url_aggregator.
4975
 *
4976
 * This file defines the `url_components` struct which stores byte offsets
4977
 * into a URL string buffer. It is used internally by `url_aggregator` to
4978
 * efficiently locate URL components without storing separate strings.
4979
 */
4980
#ifndef ADA_URL_COMPONENTS_H
4981
#define ADA_URL_COMPONENTS_H
4982
4983
#include <cstdint>
4984
#include <string>
4985
4986
namespace ada {
4987
4988
/**
4989
 * @brief Stores byte offsets for URL components within a buffer.
4990
 *
4991
 * The `url_components` struct uses 32-bit offsets to track the boundaries
4992
 * of each URL component within a single string buffer. This enables efficient
4993
 * component extraction without additional memory allocations.
4994
 *
4995
 * Component layout in a URL:
4996
 * ```
4997
 * https://user:pass@example.com:1234/foo/bar?baz#quux
4998
 *       |     |    |          | ^^^^|       |   |
4999
 *       |     |    |          | |   |       |   `----- hash_start
5000
 *       |     |    |          | |   |       `--------- search_start
5001
 *       |     |    |          | |   `----------------- pathname_start
5002
 *       |     |    |          | `--------------------- port
5003
 *       |     |    |          `----------------------- host_end
5004
 *       |     |    `---------------------------------- host_start
5005
 *       |     `--------------------------------------- username_end
5006
 *       `--------------------------------------------- protocol_end
5007
 * ```
5008
 *
5009
 * @note The 32-bit offsets limit URLs to 4GB in length.
5010
 * @note A value of `omitted` (UINT32_MAX) indicates the component is not
5011
 * present.
5012
 */
5013
struct url_components {
5014
  /** Sentinel value indicating a component is not present. */
5015
  constexpr static uint32_t omitted = uint32_t(-1);
5016
5017
1.44M
  url_components() = default;
5018
  url_components(const url_components& u) = default;
5019
  url_components(url_components&& u) noexcept = default;
5020
  url_components& operator=(url_components&& u) noexcept = default;
5021
  url_components& operator=(const url_components& u) = default;
5022
  ~url_components() = default;
5023
5024
  /** Offset of the end of the protocol/scheme (position of ':'). */
5025
  uint32_t protocol_end{0};
5026
5027
  /**
5028
   * Offset of the end of the username.
5029
   * Initialized to 0 (not `omitted`) to simplify username/password getters.
5030
   */
5031
  uint32_t username_end{0};
5032
5033
  /** Offset of the start of the host. */
5034
  uint32_t host_start{0};
5035
5036
  /** Offset of the end of the host. */
5037
  uint32_t host_end{0};
5038
5039
  /** Port number, or `omitted` if no port is specified. */
5040
  uint32_t port{omitted};
5041
5042
  /** Offset of the start of the pathname. */
5043
  uint32_t pathname_start{0};
5044
5045
  /** Offset of the '?' starting the query, or `omitted` if no query. */
5046
  uint32_t search_start{omitted};
5047
5048
  /** Offset of the '#' starting the fragment, or `omitted` if no fragment. */
5049
  uint32_t hash_start{omitted};
5050
5051
  /**
5052
   * Validates that offsets are in ascending order and consistent.
5053
   * Useful for debugging to detect internal corruption.
5054
   * @return `true` if offsets are consistent, `false` otherwise.
5055
   */
5056
  [[nodiscard]] constexpr bool check_offset_consistency() const noexcept;
5057
5058
  /**
5059
   * Returns a JSON string representation of the offsets for debugging.
5060
   * @return A JSON-formatted string with all offset values.
5061
   */
5062
  [[nodiscard]] std::string to_string() const;
5063
5064
};  // struct url_components
5065
}  // namespace ada
5066
#endif
5067
/* end file include/ada/url_components.h */
5068
5069
namespace ada {
5070
5071
struct url_aggregator;
5072
5073
// namespace parser {
5074
// template <typename result_type>
5075
// result_type parse_url(std::string_view user_input,
5076
//                       const result_type* base_url = nullptr);
5077
// template <typename result_type, bool store_values>
5078
// result_type parse_url_impl(std::string_view user_input,
5079
//                            const result_type* base_url = nullptr);
5080
// }
5081
5082
/**
5083
 * @brief Represents a parsed URL with individual string components.
5084
 *
5085
 * The `url` struct stores each URL component (scheme, username, password,
5086
 * host, port, path, query, fragment) as a separate `std::string`. This
5087
 * provides flexibility but incurs more memory allocations compared to
5088
 * `url_aggregator`.
5089
 *
5090
 * **When to use `ada::url`:**
5091
 * - When you need to frequently modify individual URL components
5092
 * - When you want independent ownership of component strings
5093
 *
5094
 * **When to use `ada::url_aggregator` instead:**
5095
 * - For read-mostly operations on parsed URLs
5096
 * - When memory efficiency is important
5097
 * - When you only need string_view access to components
5098
 *
5099
 * @note This type is returned when parsing with `ada::parse<ada::url>()`.
5100
 *       By default, `ada::parse()` returns `ada::url_aggregator`.
5101
 *
5102
 * @see url_aggregator For a more memory-efficient URL representation
5103
 * @see https://url.spec.whatwg.org/#url-representation
5104
 */
5105
struct url : url_base {
5106
0
  url() = default;
5107
0
  url(const url& u) = default;
5108
0
  url(url&& u) noexcept = default;
5109
0
  url& operator=(url&& u) noexcept = default;
5110
0
  url& operator=(const url& u) = default;
5111
0
  ~url() override = default;
5112
5113
  // Fields are ordered so that the most frequently accessed components
5114
  // tend to occupy earlier cache lines and remain close together in memory.
5115
  //
5116
  // Note: The exact object layout (including cache-line boundaries, byte
5117
  // offsets, and member sizes) is implementation- and platform-dependent.
5118
  // This ordering expresses an intent for better cache locality but does not
5119
  // guarantee any specific in-memory layout.
5120
5121
  /**
5122
   * @private
5123
   * A URL's host is null or a host. It is initially null.
5124
   */
5125
  std::optional<std::string> host{};
5126
5127
  /**
5128
   * @private
5129
   * A URL's path is either an ASCII string or a list of zero or more ASCII
5130
   * strings, usually identifying a location.
5131
   */
5132
  std::string path{};
5133
5134
  /**
5135
   * @private
5136
   * A URL's query is either null or an ASCII string. It is initially null.
5137
   */
5138
  std::optional<std::string> query{};
5139
5140
  /**
5141
   * @private
5142
   * A URL's fragment is either null or an ASCII string that can be used for
5143
   * further processing on the resource the URL's other components identify. It
5144
   * is initially null.
5145
   */
5146
  std::optional<std::string> hash{};
5147
5148
  /**
5149
   * @private
5150
   * A URL's port is either null or a 16-bit unsigned integer that identifies a
5151
   * networking port. It is initially null.
5152
   */
5153
  std::optional<uint16_t> port{};
5154
5155
  /**
5156
   * @private
5157
   * A URL's username is an ASCII string identifying a username. It is initially
5158
   * the empty string.
5159
   */
5160
  std::string username{};
5161
5162
  /**
5163
   * @private
5164
   * A URL's password is an ASCII string identifying a password. It is initially
5165
   * the empty string.
5166
   */
5167
  std::string password{};
5168
5169
  /**
5170
   * Checks if the URL has an empty hostname (host is set but empty string).
5171
   * @return `true` if host exists but is empty, `false` otherwise.
5172
   */
5173
  [[nodiscard]] inline bool has_empty_hostname() const noexcept;
5174
5175
  /**
5176
   * Checks if the URL has a non-default port explicitly specified.
5177
   * @return `true` if a port is present, `false` otherwise.
5178
   */
5179
  [[nodiscard]] inline bool has_port() const noexcept;
5180
5181
  /**
5182
   * Checks if the URL has a hostname (including empty hostnames).
5183
   * @return `true` if host is present, `false` otherwise.
5184
   */
5185
  [[nodiscard]] inline bool has_hostname() const noexcept;
5186
5187
  /**
5188
   * Validates whether the hostname is a valid domain according to RFC 1034.
5189
   * Checks that the domain and its labels have valid lengths (max 255 octets
5190
   * total, max 63 octets per label).
5191
   * @return `true` if the domain is valid, `false` otherwise.
5192
   */
5193
  [[nodiscard]] bool has_valid_domain() const noexcept override;
5194
5195
  /**
5196
   * Returns a JSON string representation of this URL for debugging.
5197
   * @return A JSON-formatted string with all URL components.
5198
   */
5199
  [[nodiscard]] std::string to_string() const override;
5200
5201
  /**
5202
   * Returns the full serialized URL (the href).
5203
   * @return The complete URL string (allocates a new string).
5204
   * @see https://url.spec.whatwg.org/#dom-url-href
5205
   */
5206
  [[nodiscard]] ada_really_inline std::string get_href() const;
5207
5208
  /**
5209
   * Returns the byte length of the serialized URL without allocating a string.
5210
   * @return Size of the href in bytes.
5211
   */
5212
  [[nodiscard]] size_t get_href_size() const noexcept;
5213
5214
  /**
5215
   * Returns the URL's origin as a string (scheme + host + port for special
5216
   * URLs).
5217
   * @return A newly allocated string containing the serialized origin.
5218
   * @see https://url.spec.whatwg.org/#concept-url-origin
5219
   */
5220
  [[nodiscard]] std::string get_origin() const override;
5221
5222
  /**
5223
   * Returns the URL's scheme followed by a colon (e.g., "https:").
5224
   * @return A newly allocated string with the protocol.
5225
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5226
   */
5227
  [[nodiscard]] std::string get_protocol() const;
5228
5229
  /**
5230
   * Returns the URL's host and port (e.g., "example.com:8080").
5231
   * If no port is set, returns just the host. Returns empty string if no host.
5232
   * @return A newly allocated string with host:port.
5233
   * @see https://url.spec.whatwg.org/#dom-url-host
5234
   */
5235
  [[nodiscard]] std::string get_host() const;
5236
5237
  /**
5238
   * Returns the URL's hostname (without port).
5239
   * Returns empty string if no host is set.
5240
   * @return A newly allocated string with the hostname.
5241
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5242
   */
5243
  [[nodiscard]] std::string get_hostname() const;
5244
5245
  /**
5246
   * Returns the URL's path component.
5247
   * @return A string_view pointing to the path.
5248
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5249
   */
5250
  [[nodiscard]] constexpr std::string_view get_pathname() const noexcept;
5251
5252
  /**
5253
   * Returns the byte length of the pathname without creating a string.
5254
   * @return Size of the pathname in bytes.
5255
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5256
   */
5257
  [[nodiscard]] ada_really_inline size_t get_pathname_length() const noexcept;
5258
5259
  /**
5260
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
5261
   * Returns empty string if no query is set.
5262
   * @return A newly allocated string with the search/query.
5263
   * @see https://url.spec.whatwg.org/#dom-url-search
5264
   */
5265
  [[nodiscard]] std::string get_search() const;
5266
5267
  /**
5268
   * Returns the URL's username component.
5269
   * @return A constant reference to the username string.
5270
   * @see https://url.spec.whatwg.org/#dom-url-username
5271
   */
5272
  [[nodiscard]] const std::string& get_username() const noexcept;
5273
5274
  /**
5275
   * Sets the URL's username, percent-encoding special characters.
5276
   * @param input The new username value.
5277
   * @return `true` on success, `false` if the URL cannot have credentials.
5278
   * @see https://url.spec.whatwg.org/#dom-url-username
5279
   */
5280
  bool set_username(std::string_view input);
5281
5282
  /**
5283
   * Sets the URL's password, percent-encoding special characters.
5284
   * @param input The new password value.
5285
   * @return `true` on success, `false` if the URL cannot have credentials.
5286
   * @see https://url.spec.whatwg.org/#dom-url-password
5287
   */
5288
  bool set_password(std::string_view input);
5289
5290
  /**
5291
   * Sets the URL's port from a string (e.g., "8080").
5292
   * @param input The port string. Empty string removes the port.
5293
   * @return `true` on success, `false` if the URL cannot have a port.
5294
   * @see https://url.spec.whatwg.org/#dom-url-port
5295
   */
5296
  bool set_port(std::string_view input);
5297
5298
  /**
5299
   * Sets the URL's fragment/hash (the part after '#').
5300
   * @param input The new hash value (with or without leading '#').
5301
   * @see https://url.spec.whatwg.org/#dom-url-hash
5302
   */
5303
  void set_hash(std::string_view input);
5304
5305
  /**
5306
   * Sets the URL's query string (the part after '?').
5307
   * @param input The new query value (with or without leading '?').
5308
   * @see https://url.spec.whatwg.org/#dom-url-search
5309
   */
5310
  void set_search(std::string_view input);
5311
5312
  /**
5313
   * Sets the URL's pathname.
5314
   * @param input The new path value.
5315
   * @return `true` on success, `false` if the URL has an opaque path.
5316
   * @see https://url.spec.whatwg.org/#dom-url-pathname
5317
   */
5318
  bool set_pathname(std::string_view input);
5319
5320
  /**
5321
   * Sets the URL's host (hostname and optionally port).
5322
   * @param input The new host value (e.g., "example.com:8080").
5323
   * @return `true` on success, `false` if parsing fails.
5324
   * @see https://url.spec.whatwg.org/#dom-url-host
5325
   */
5326
  bool set_host(std::string_view input);
5327
5328
  /**
5329
   * Sets the URL's hostname (without port).
5330
   * @param input The new hostname value.
5331
   * @return `true` on success, `false` if parsing fails.
5332
   * @see https://url.spec.whatwg.org/#dom-url-hostname
5333
   */
5334
  bool set_hostname(std::string_view input);
5335
5336
  /**
5337
   * Sets the URL's protocol/scheme.
5338
   * @param input The new protocol (with or without trailing ':').
5339
   * @return `true` on success, `false` if the scheme is invalid.
5340
   * @see https://url.spec.whatwg.org/#dom-url-protocol
5341
   */
5342
  bool set_protocol(std::string_view input);
5343
5344
  /**
5345
   * Replaces the entire URL by parsing a new href string.
5346
   * @param input The new URL string to parse.
5347
   * @return `true` on success, `false` if parsing fails.
5348
   * @see https://url.spec.whatwg.org/#dom-url-href
5349
   */
5350
  bool set_href(std::string_view input);
5351
5352
  /**
5353
   * Returns the URL's password component.
5354
   * @return A constant reference to the password string.
5355
   * @see https://url.spec.whatwg.org/#dom-url-password
5356
   */
5357
  [[nodiscard]] const std::string& get_password() const noexcept;
5358
5359
  /**
5360
   * Returns the URL's port as a string (e.g., "8080").
5361
   * Returns empty string if no port is set.
5362
   * @return A newly allocated string with the port.
5363
   * @see https://url.spec.whatwg.org/#dom-url-port
5364
   */
5365
  [[nodiscard]] std::string get_port() const;
5366
5367
  /**
5368
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
5369
   * Returns empty string if no fragment is set.
5370
   * @return A newly allocated string with the hash.
5371
   * @see https://url.spec.whatwg.org/#dom-url-hash
5372
   */
5373
  [[nodiscard]] std::string get_hash() const;
5374
5375
  /**
5376
   * Checks if the URL has credentials (non-empty username or password).
5377
   * @return `true` if username or password is non-empty, `false` otherwise.
5378
   */
5379
  [[nodiscard]] ada_really_inline bool has_credentials() const noexcept;
5380
5381
  /**
5382
   * Returns the URL component offsets for efficient serialization.
5383
   *
5384
   * The components represent byte offsets into the serialized URL:
5385
   * ```
5386
   * https://user:pass@example.com:1234/foo/bar?baz#quux
5387
   *       |     |    |          | ^^^^|       |   |
5388
   *       |     |    |          | |   |       |   `----- hash_start
5389
   *       |     |    |          | |   |       `--------- search_start
5390
   *       |     |    |          | |   `----------------- pathname_start
5391
   *       |     |    |          | `--------------------- port
5392
   *       |     |    |          `----------------------- host_end
5393
   *       |     |    `---------------------------------- host_start
5394
   *       |     `--------------------------------------- username_end
5395
   *       `--------------------------------------------- protocol_end
5396
   * ```
5397
   * @return A newly constructed url_components struct.
5398
   * @see https://github.com/servo/rust-url
5399
   */
5400
  [[nodiscard]] ada_really_inline ada::url_components get_components() const;
5401
5402
  /**
5403
   * Checks if the URL has a fragment/hash component.
5404
   * @return `true` if hash is present, `false` otherwise.
5405
   */
5406
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
5407
5408
  /**
5409
   * Checks if the URL has a query/search component.
5410
   * @return `true` if query is present, `false` otherwise.
5411
   */
5412
  [[nodiscard]] constexpr bool has_search() const noexcept override;
5413
5414
 private:
5415
  friend ada::url ada::parser::parse_url<ada::url>(std::string_view,
5416
                                                   const ada::url*);
5417
  friend ada::url_aggregator ada::parser::parse_url<ada::url_aggregator>(
5418
      std::string_view, const ada::url_aggregator*);
5419
  friend void ada::helpers::strip_trailing_spaces_from_opaque_path<ada::url>(
5420
      ada::url& url);
5421
5422
  friend ada::url ada::parser::parse_url_impl<ada::url, true>(std::string_view,
5423
                                                              const ada::url*);
5424
  friend ada::url_aggregator ada::parser::parse_url_impl<
5425
      ada::url_aggregator, true>(std::string_view, const ada::url_aggregator*);
5426
  friend ada::url_aggregator ada::parser::parse_url_impl<
5427
      ada::url_aggregator, false>(std::string_view, const ada::url_aggregator*);
5428
  template <class result_type>
5429
  friend bool ada::parser::try_parse_simple_absolute(std::string_view,
5430
                                                     result_type&);
5431
  template <class result_type>
5432
  friend bool ada::parser::finish_simple_absolute_with_port(
5433
      std::string_view, result_type&, ada::scheme::type, uint32_t, size_t,
5434
      size_t, size_t, bool);
5435
  template <class result_type>
5436
  friend bool ada::parser::try_parse_simple_relative(std::string_view,
5437
                                                     const result_type&,
5438
                                                     result_type&);
5439
5440
  inline void update_unencoded_base_hash(std::string_view input);
5441
  inline void update_base_hostname(std::string_view input);
5442
  inline void update_base_search(std::string_view input,
5443
                                 const uint8_t query_percent_encode_set[]);
5444
  inline void update_base_search(std::optional<std::string>&& input);
5445
  inline void update_base_pathname(std::string_view input);
5446
  inline void update_base_username(std::string_view input);
5447
  inline void update_base_password(std::string_view input);
5448
  inline void update_base_port(std::optional<uint16_t> input);
5449
5450
  /**
5451
   * Returns true if growing href by input_len (worst case) could exceed
5452
   * get_max_input_length(), meaning a rollback snapshot is actually needed.
5453
   */
5454
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
5455
5456
  /**
5457
   * Sets the host or hostname according to override condition.
5458
   * Return true on success.
5459
   * @see https://url.spec.whatwg.org/#hostname-state
5460
   */
5461
  template <bool override_hostname = false>
5462
  bool set_host_or_hostname(std::string_view input);
5463
5464
  /**
5465
   * Return true on success.
5466
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
5467
   */
5468
  [[nodiscard]] bool parse_ipv4(std::string_view input);
5469
5470
  /**
5471
   * Return true on success.
5472
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
5473
   */
5474
  [[nodiscard]] bool parse_ipv6(std::string_view input);
5475
5476
  /**
5477
   * Return true on success.
5478
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
5479
   */
5480
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
5481
5482
  /**
5483
   * A URL's scheme is an ASCII string that identifies the type of URL and can
5484
   * be used to dispatch a URL for further processing after parsing. It is
5485
   * initially the empty string. We only set non_special_scheme when the scheme
5486
   * is non-special, otherwise we avoid constructing string.
5487
   *
5488
   * Special schemes are stored in ada::scheme::details::is_special_list so we
5489
   * typically do not need to store them in each url instance.
5490
   */
5491
  std::string non_special_scheme{};
5492
5493
  /**
5494
   * A URL cannot have a username/password/port if its host is null or the empty
5495
   * string, or its scheme is "file".
5496
   */
5497
  [[nodiscard]] inline bool cannot_have_credentials_or_port() const;
5498
5499
  ada_really_inline size_t parse_port(
5500
      std::string_view view, bool check_trailing_content) noexcept override;
5501
5502
0
  ada_really_inline size_t parse_port(std::string_view view) noexcept override {
5503
0
    return this->parse_port(view, false);
5504
0
  }
5505
5506
  /**
5507
   * Parse the host from the provided input. We assume that
5508
   * the input does not contain spaces or tabs. Control
5509
   * characters and spaces are not trimmed (they should have
5510
   * been removed if needed).
5511
   * Return true on success.
5512
   * @see https://url.spec.whatwg.org/#host-parsing
5513
   */
5514
  [[nodiscard]] ada_really_inline bool parse_host(std::string_view input);
5515
5516
  template <bool has_state_override = false>
5517
  [[nodiscard]] ada_really_inline bool parse_scheme(std::string_view input);
5518
5519
  constexpr void clear_pathname() override;
5520
  constexpr void clear_search() override;
5521
  constexpr void set_protocol_as_file();
5522
5523
  /**
5524
   * Parse the path from the provided input.
5525
   * Return true on success. Control characters not
5526
   * trimmed from the ends (they should have
5527
   * been removed if needed).
5528
   *
5529
   * The input is expected to be UTF-8.
5530
   *
5531
   * @see https://url.spec.whatwg.org/
5532
   */
5533
  ada_really_inline void parse_path(std::string_view input);
5534
5535
  /**
5536
   * Set the scheme for this URL. The provided scheme should be a valid
5537
   * scheme string, be lower-cased, not contain spaces or tabs. It should
5538
   * have no spurious trailing or leading content.
5539
   */
5540
  inline void set_scheme(std::string&& new_scheme) noexcept;
5541
5542
  /**
5543
   * Take the scheme from another URL. The scheme string is moved from the
5544
   * provided url.
5545
   */
5546
  constexpr void copy_scheme(ada::url&& u);
5547
5548
  /**
5549
   * Take the scheme from another URL. The scheme string is copied from the
5550
   * provided url.
5551
   */
5552
  constexpr void copy_scheme(const ada::url& u);
5553
5554
};  // struct url
5555
5556
inline std::ostream& operator<<(std::ostream& out, const ada::url& u);
5557
}  // namespace ada
5558
5559
#endif  // ADA_URL_H
5560
/* end file include/ada/url.h */
5561
5562
namespace ada {
5563
5564
/**
5565
 * Result type for URL parsing operations.
5566
 *
5567
 * Uses `tl::expected` to represent either a successfully parsed URL or an
5568
 * error. This allows for exception-free error handling.
5569
 *
5570
 * @tparam result_type The URL type to return (default: `ada::url_aggregator`)
5571
 *
5572
 * @example
5573
 * ```cpp
5574
 * ada::result<ada::url_aggregator> result = ada::parse("https://example.com");
5575
 * if (result) {
5576
 *     // Success: use result.value() or *result
5577
 * } else {
5578
 *     // Error: handle result.error()
5579
 * }
5580
 * ```
5581
 */
5582
template <class result_type = ada::url_aggregator>
5583
using result = tl::expected<result_type, ada::errors>;
5584
5585
/**
5586
 * Parses a URL string according to the WHATWG URL Standard.
5587
 *
5588
 * This is the main entry point for URL parsing in Ada. The function takes
5589
 * a string input and optionally a base URL for resolving relative URLs.
5590
 *
5591
 * @tparam result_type The URL type to return. Can be either `ada::url` or
5592
 *         `ada::url_aggregator` (default). The `url_aggregator` type is more
5593
 *         memory-efficient as it stores components as offsets into a single
5594
 *         buffer.
5595
 *
5596
 * @param input The URL string to parse. Must be valid ASCII or UTF-8 encoded.
5597
 *        Leading and trailing whitespace is automatically trimmed.
5598
 * @param base_url Optional pointer to a base URL for resolving relative URLs.
5599
 *        If nullptr (default), only absolute URLs can be parsed successfully.
5600
 *
5601
 * @return A `result<result_type>` containing either the parsed URL on success,
5602
 *         or an error code on failure. Use the boolean conversion or
5603
 *         `has_value()` to check for success.
5604
 *
5605
 * @note The parser is fully compliant with the WHATWG URL Standard.
5606
 *
5607
 * Parsing fails if the input or the resulting normalized URL exceeds
5608
 * `get_max_input_length()` bytes (default ~4 GB, configurable via
5609
 * `set_max_input_length()`). This accounts for percent-encoding expansion:
5610
 * a short input that normalizes into a long URL is still rejected.
5611
 *
5612
 * @example
5613
 * ```cpp
5614
 * // Parse an absolute URL
5615
 * auto url = ada::parse("https://user:pass@example.com:8080/path?query#hash");
5616
 * if (url) {
5617
 *     std::cout << url->get_hostname(); // "example.com"
5618
 *     std::cout << url->get_pathname(); // "/path"
5619
 * }
5620
 *
5621
 * // Parse a relative URL with a base
5622
 * auto base = ada::parse("https://example.com/dir/");
5623
 * if (base) {
5624
 *     auto relative = ada::parse("../other/page", &*base);
5625
 *     if (relative) {
5626
 *         std::cout << relative->get_href(); //
5627
 * "https://example.com/other/page"
5628
 *     }
5629
 * }
5630
 * ```
5631
 *
5632
 * @see https://url.spec.whatwg.org/#url-parsing
5633
 */
5634
template <class result_type = ada::url_aggregator>
5635
ada_warn_unused ada::result<result_type> parse(
5636
    std::string_view input, const result_type* base_url = nullptr);
5637
5638
extern template ada::result<url> parse<url>(std::string_view input,
5639
                                            const url* base_url);
5640
extern template ada::result<url_aggregator> parse<url_aggregator>(
5641
    std::string_view input, const url_aggregator* base_url);
5642
5643
/**
5644
 * Checks whether a URL string can be successfully parsed.
5645
 *
5646
 * Equivalent to `parse(input, base).has_value()` for every input, including
5647
 * when `set_max_input_length` rejects a normalized href that exceeds the limit.
5648
 *
5649
 * @param input The URL string to validate. Must be valid ASCII or UTF-8.
5650
 * @param base_input Optional base URL string for relative inputs.
5651
 * @return `true` if parsing would succeed, `false` otherwise.
5652
 * @see https://url.spec.whatwg.org/#dom-url-canparse
5653
 */
5654
bool can_parse(std::string_view input,
5655
               const std::string_view* base_input = nullptr);
5656
5657
#if ADA_INCLUDE_URL_PATTERN
5658
/**
5659
 * Parses a URL pattern according to the URLPattern specification.
5660
 *
5661
 * URL patterns provide a syntax for matching URLs against patterns, similar
5662
 * to how regular expressions match strings. This is useful for routing and
5663
 * URL-based dispatching.
5664
 *
5665
 * @tparam regex_provider The regex implementation to use for pattern matching.
5666
 *
5667
 * @param input Either a URL pattern string (valid UTF-8) or a URLPatternInit
5668
 *        struct specifying individual component patterns.
5669
 * @param base_url Optional pointer to a base URL string (valid UTF-8) for
5670
 *        resolving relative patterns.
5671
 * @param options Optional pointer to configuration options (e.g., ignore_case).
5672
 *
5673
 * @return A `tl::expected` containing either the parsed url_pattern on success,
5674
 *         or an error code on failure.
5675
 *
5676
 * @see https://urlpattern.spec.whatwg.org
5677
 */
5678
template <url_pattern_regex::regex_concept regex_provider>
5679
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
5680
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
5681
                  const std::string_view* base_url = nullptr,
5682
                  const url_pattern_options* options = nullptr);
5683
5684
template <url_pattern_regex::regex_concept regex_provider>
5685
class url_pattern_list;
5686
5687
/**
5688
 * Compiles a set of URLPattern pathname patterns into an ada::url_pattern_list
5689
 * (experimental): one structure that answers "which route matches this
5690
 * pathname, and what are its group values?" without a loop over the
5691
 * patterns and without executing regular expressions for routes written in
5692
 * the static / ":param" / "*" subset.
5693
 *
5694
 * @tparam regex_provider The regex implementation, as for parse_url_pattern;
5695
 *         it is used only for routes that need URLPattern regexp semantics.
5696
 *
5697
 * @param pathname_patterns URLPattern pathname patterns (valid UTF-8), for
5698
 *        example "/users/:id". The index of a pattern is the route index
5699
 *        reported by url_pattern_list::match; duplicates are allowed.
5700
 * @param base_url Optional pointer to a base URL string (valid UTF-8): each
5701
 *        pattern is then processed as the pathname of a URLPatternInit with
5702
 *        that base URL (relative patterns resolve against its path).
5703
 * @param options Optional pointer to configuration options (ignore_case),
5704
 *        applied to every route as parse_url_pattern applies them.
5705
 *
5706
 * @return A `tl::expected` containing either the compiled list on success,
5707
 *         or an error code when a pattern is not a valid URLPattern pathname
5708
 *         pattern (errors::type_error, as the URLPattern constructor).
5709
 *
5710
 * @see https://github.com/whatwg/urlpattern/issues/166
5711
 */
5712
template <url_pattern_regex::regex_concept regex_provider>
5713
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5714
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
5715
                       const std::string_view* base_url = nullptr,
5716
                       const url_pattern_options* options = nullptr);
5717
5718
/**
5719
 * Compiles the pathname components of existing url_pattern objects into an
5720
 * ada::url_pattern_list (experimental). Only the pathname component of each
5721
 * pattern is used; routes that need the regex provider reuse the pattern's
5722
 * already compiled pathname component. All patterns must share the same
5723
 * ignore_case setting (errors::type_error otherwise); the pathname is
5724
 * compiled as for a special-scheme URL.
5725
 */
5726
template <url_pattern_regex::regex_concept regex_provider>
5727
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
5728
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
5729
#endif  // ADA_INCLUDE_URL_PATTERN
5730
5731
/**
5732
 * Converts a file system path to a file:// URL.
5733
 *
5734
 * Creates a properly formatted file URL from a local file system path.
5735
 * Handles platform-specific path separators and percent-encoding.
5736
 *
5737
 * Respects `get_max_input_length()`: if the input path or the resulting
5738
 * `file://` href would exceed the limit, returns an empty string.
5739
 *
5740
 * @param path The file system path to convert. Must be valid ASCII or UTF-8.
5741
 *
5742
 * @return A file:// URL string representing the given path, or empty on
5743
 *         length-limit rejection.
5744
 */
5745
std::string href_from_file(std::string_view path);
5746
5747
/**
5748
 * Sets the maximum allowed length for URLs.
5749
 *
5750
 * Both the raw input and the resulting normalized URL (the href) are checked
5751
 * against this limit. Parsing or setter calls that would produce a URL
5752
 * exceeding this length are rejected. The same limit also applies to
5753
 * `href_from_file` and to query strings passed to `url_search_params`
5754
 * construction / `reset`. The value must fit in a uint32_t.
5755
 * The default is std::numeric_limits<uint32_t>::max() (approximately 4 GB).
5756
 *
5757
 * @param length The new maximum URL length in bytes.
5758
 */
5759
void set_max_input_length(uint32_t length);
5760
5761
/**
5762
 * Returns the current maximum allowed length for URLs.
5763
 *
5764
 * @return The current maximum URL length in bytes.
5765
 */
5766
uint32_t get_max_input_length();
5767
5768
}  // namespace ada
5769
5770
#endif  // ADA_IMPLEMENTATION_H
5771
/* end file include/ada/implementation.h */
5772
5773
#include <cstdint>
5774
#include <optional>
5775
#include <ostream>
5776
#include <string>
5777
#include <string_view>
5778
#include <unordered_map>
5779
#include <utility>
5780
#include <variant>
5781
#include <vector>
5782
5783
#if ADA_TESTING
5784
#include <iostream>
5785
#endif  // ADA_TESTING
5786
5787
#if ADA_INCLUDE_URL_PATTERN
5788
namespace ada {
5789
5790
enum class url_pattern_part_type : uint8_t {
5791
  // The part represents a simple fixed text string.
5792
  FIXED_TEXT,
5793
  // The part represents a matching group with a custom regular expression.
5794
  REGEXP,
5795
  // The part represents a matching group that matches code points up to the
5796
  // next separator code point. This is typically used for a named group like
5797
  // ":foo" that does not have a custom regular expression.
5798
  SEGMENT_WILDCARD,
5799
  // The part represents a matching group that greedily matches all code points.
5800
  // This is typically used for the "*" wildcard matching group.
5801
  FULL_WILDCARD,
5802
};
5803
5804
// Pattern type for fast-path matching optimization.
5805
// This allows skipping expensive regex evaluation for common simple patterns.
5806
enum class url_pattern_component_type : uint8_t {
5807
  // Pattern is "^$" - only matches empty string
5808
  EMPTY,
5809
  // Pattern is "^<literal>$" - exact string match (no regex needed)
5810
  EXACT_MATCH,
5811
  // Pattern is "^(.*)$" - matches anything (full wildcard)
5812
  FULL_WILDCARD,
5813
  // Pattern requires actual regex evaluation
5814
  REGEXP,
5815
};
5816
5817
enum class url_pattern_part_modifier : uint8_t {
5818
  // The part does not have a modifier.
5819
  none,
5820
  // The part has an optional modifier indicated by the U+003F (?) code point.
5821
  optional,
5822
  // The part has a "zero or more" modifier indicated by the U+002A (*) code
5823
  // point.
5824
  zero_or_more,
5825
  // The part has a "one or more" modifier indicated by the U+002B (+) code
5826
  // point.
5827
  one_or_more,
5828
};
5829
5830
// @see https://urlpattern.spec.whatwg.org/#part
5831
class url_pattern_part {
5832
 public:
5833
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5834
                   url_pattern_part_modifier _modifier)
5835
263k
      : type(_type), value(std::move(_value)), modifier(_modifier) {}
5836
5837
  url_pattern_part(url_pattern_part_type _type, std::string&& _value,
5838
                   url_pattern_part_modifier _modifier, std::string&& _name,
5839
                   std::string&& _prefix, std::string&& _suffix)
5840
592k
      : type(_type),
5841
592k
        value(std::move(_value)),
5842
592k
        modifier(_modifier),
5843
592k
        name(std::move(_name)),
5844
592k
        prefix(std::move(_prefix)),
5845
592k
        suffix(std::move(_suffix)) {}
5846
  // A part has an associated type, a string, which must be set upon creation.
5847
  url_pattern_part_type type;
5848
  // A part has an associated value, a string, which must be set upon creation.
5849
  std::string value;
5850
  // A part has an associated modifier a string, which must be set upon
5851
  // creation.
5852
  url_pattern_part_modifier modifier;
5853
  // A part has an associated name, a string, initially the empty string.
5854
  std::string name{};
5855
  // A part has an associated prefix, a string, initially the empty string.
5856
  std::string prefix{};
5857
  // A part has an associated suffix, a string, initially the empty string.
5858
  std::string suffix{};
5859
5860
  inline bool is_regexp() const noexcept;
5861
};
5862
5863
// @see https://urlpattern.spec.whatwg.org/#options-header
5864
struct url_pattern_compile_component_options {
5865
  url_pattern_compile_component_options() = default;
5866
  explicit url_pattern_compile_component_options(
5867
      std::optional<char> new_delimiter = std::nullopt,
5868
      std::optional<char> new_prefix = std::nullopt) noexcept
5869
6
      : delimiter(new_delimiter), prefix(new_prefix) {}
5870
5871
  inline std::string_view get_delimiter() const ada_warn_unused;
5872
  inline std::string_view get_prefix() const ada_warn_unused;
5873
5874
  // @see https://urlpattern.spec.whatwg.org/#options-ignore-case
5875
  bool ignore_case = false;
5876
5877
  static url_pattern_compile_component_options DEFAULT;
5878
  static url_pattern_compile_component_options HOSTNAME;
5879
  static url_pattern_compile_component_options PATHNAME;
5880
5881
 private:
5882
  // @see https://urlpattern.spec.whatwg.org/#options-delimiter-code-point
5883
  std::optional<char> delimiter{};
5884
  // @see https://urlpattern.spec.whatwg.org/#options-prefix-code-point
5885
  std::optional<char> prefix{};
5886
};
5887
5888
// The default options is an options struct with delimiter code point set to
5889
// the empty string and prefix code point set to the empty string.
5890
inline url_pattern_compile_component_options
5891
    url_pattern_compile_component_options::DEFAULT(std::nullopt, std::nullopt);
5892
5893
// The hostname options is an options struct with delimiter code point set
5894
// "." and prefix code point set to the empty string.
5895
inline url_pattern_compile_component_options
5896
    url_pattern_compile_component_options::HOSTNAME('.', std::nullopt);
5897
5898
// The pathname options is an options struct with delimiter code point set
5899
// "/" and prefix code point set to "/".
5900
inline url_pattern_compile_component_options
5901
    url_pattern_compile_component_options::PATHNAME('/', '/');
5902
5903
// A struct providing the URLPattern matching results for a single
5904
// URL component. The URLPatternComponentResult is only ever used
5905
// as a member attribute of a URLPatternResult struct. The
5906
// URLPatternComponentResult API is defined as part of the URLPattern
5907
// specification.
5908
struct url_pattern_component_result {
5909
  std::string input;
5910
  std::unordered_map<std::string, std::optional<std::string>> groups;
5911
5912
  bool operator==(const url_pattern_component_result&) const;
5913
5914
#if ADA_TESTING
5915
  friend void PrintTo(const url_pattern_component_result& result,
5916
                      std::ostream* os) {
5917
    *os << "input: '" << result.input << "', group: ";
5918
    for (const auto& group : result.groups) {
5919
      *os << "(" << group.first << ", " << group.second.value_or("undefined")
5920
          << ") ";
5921
    }
5922
  }
5923
#endif  // ADA_TESTING
5924
};
5925
5926
template <url_pattern_regex::regex_concept regex_provider>
5927
class url_pattern_component {
5928
 public:
5929
1.09M
  url_pattern_component() = default;
5930
5931
  // This function explicitly takes a std::string because it is moved.
5932
  // To avoid unnecessary copy, move each value while calling the constructor.
5933
  url_pattern_component(std::string&& new_pattern,
5934
                        typename regex_provider::regex_type&& new_regexp,
5935
                        std::vector<std::string>&& new_group_name_list,
5936
                        bool new_has_regexp_groups,
5937
                        url_pattern_component_type new_type,
5938
                        std::string&& new_exact_match_value = {})
5939
1.10M
      : regexp(std::move(new_regexp)),
5940
1.10M
        pattern(std::move(new_pattern)),
5941
1.10M
        group_name_list(std::move(new_group_name_list)),
5942
1.10M
        exact_match_value(std::move(new_exact_match_value)),
5943
1.10M
        has_regexp_groups(new_has_regexp_groups),
5944
1.10M
        type(new_type) {}
5945
5946
  // @see https://urlpattern.spec.whatwg.org/#compile-a-component
5947
  template <url_pattern_encoding_callback F>
5948
  static tl::expected<url_pattern_component, errors> compile(
5949
      std::string_view input, F& encoding_callback,
5950
      url_pattern_compile_component_options& options);
5951
5952
  // @see https://urlpattern.spec.whatwg.org/#create-a-component-match-result
5953
  url_pattern_component_result create_component_match_result(
5954
      std::string&& input,
5955
      std::vector<std::optional<std::string>>&& exec_result);
5956
5957
  // Fast path test that returns true/false without constructing result groups.
5958
  // Uses cached pattern type to skip regex evaluation for simple patterns.
5959
  // Note: Not marked noexcept because the regex provider may throw.
5960
  bool fast_test(std::string_view input) const;
5961
5962
  // Fast path match that returns capture groups without regex for simple
5963
  // patterns. Returns nullopt if pattern doesn't match, otherwise returns
5964
  // capture groups.
5965
  std::optional<std::vector<std::optional<std::string>>> fast_match(
5966
      std::string_view input) const;
5967
5968
#if ADA_TESTING
5969
  friend void PrintTo(const url_pattern_component& component,
5970
                      std::ostream* os) {
5971
    *os << "pattern: '" << component.pattern
5972
        << "', has_regexp_groups: " << component.has_regexp_groups
5973
        << "group_name_list: ";
5974
    for (const auto& name : component.group_name_list) {
5975
      *os << name << ", ";
5976
    }
5977
  }
5978
#endif  // ADA_TESTING
5979
5980
  typename regex_provider::regex_type regexp{};
5981
  std::string pattern{};
5982
  std::vector<std::string> group_name_list{};
5983
  // For EXACT_MATCH type: the literal string to compare against
5984
  std::string exact_match_value{};
5985
  bool has_regexp_groups = false;
5986
  // Cached pattern type for fast-path optimization
5987
  url_pattern_component_type type = url_pattern_component_type::REGEXP;
5988
};
5989
5990
// A URLPattern input can be either a string or a URLPatternInit object.
5991
// If it is a string, it must be a valid UTF-8 string.
5992
using url_pattern_input = std::variant<std::string_view, url_pattern_init>;
5993
5994
// A struct providing the URLPattern matching results for all
5995
// components of a URL. The URLPatternResult API is defined as
5996
// part of the URLPattern specification.
5997
struct url_pattern_result {
5998
  std::vector<url_pattern_input> inputs;
5999
  url_pattern_component_result protocol;
6000
  url_pattern_component_result username;
6001
  url_pattern_component_result password;
6002
  url_pattern_component_result hostname;
6003
  url_pattern_component_result port;
6004
  url_pattern_component_result pathname;
6005
  url_pattern_component_result search;
6006
  url_pattern_component_result hash;
6007
};
6008
6009
struct url_pattern_options {
6010
  bool ignore_case = false;
6011
6012
#if ADA_TESTING
6013
  friend void PrintTo(const url_pattern_options& options, std::ostream* os) {
6014
    *os << "ignore_case: '" << options.ignore_case;
6015
  }
6016
#endif  // ADA_TESTING
6017
};
6018
6019
/**
6020
 * @brief URL pattern matching class implementing the URLPattern API.
6021
 *
6022
 * URLPattern provides a way to match URLs against patterns with wildcards
6023
 * and named capture groups. It's useful for routing, URL-based dispatching,
6024
 * and URL validation.
6025
 *
6026
 * Pattern syntax supports:
6027
 * - Literal text matching
6028
 * - Named groups: `:name` (matches up to the next separator)
6029
 * - Wildcards: `*` (matches everything)
6030
 * - Custom regex: `(pattern)`
6031
 * - Optional segments: `:name?`
6032
 * - Repeated segments: `:name+`, `:name*`
6033
 *
6034
 * @tparam regex_provider The regex implementation to use for pattern matching.
6035
 *         Must satisfy the url_pattern_regex::regex_concept.
6036
 *
6037
 * @note All string inputs must be valid UTF-8.
6038
 *
6039
 * @see https://urlpattern.spec.whatwg.org/
6040
 */
6041
template <url_pattern_regex::regex_concept regex_provider>
6042
class url_pattern {
6043
 public:
6044
136k
  url_pattern() = default;
6045
6046
  /**
6047
   * If non-null, base_url must pointer at a valid UTF-8 string.
6048
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec
6049
   */
6050
  result<std::optional<url_pattern_result>> exec(
6051
      const url_pattern_input& input,
6052
      const std::string_view* base_url = nullptr);
6053
6054
  /**
6055
   * If non-null, base_url must pointer at a valid UTF-8 string.
6056
   * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test
6057
   */
6058
  result<bool> test(const url_pattern_input& input,
6059
                    const std::string_view* base_url = nullptr);
6060
6061
  /**
6062
   * @see https://urlpattern.spec.whatwg.org/#url-pattern-match
6063
   * This function expects a valid UTF-8 string if input is a string.
6064
   */
6065
  result<std::optional<url_pattern_result>> match(
6066
      const url_pattern_input& input,
6067
      const std::string_view* base_url_string = nullptr);
6068
6069
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol
6070
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
6071
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-username
6072
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
6073
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-password
6074
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
6075
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname
6076
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
6077
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-port
6078
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
6079
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname
6080
  [[nodiscard]] std::string_view get_pathname() const ada_lifetime_bound;
6081
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-search
6082
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
6083
  // @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash
6084
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
6085
6086
  // If ignoreCase is true, the JavaScript regular expression created for each
6087
  // pattern must use the `vi` flag. Otherwise, they must use the `v` flag.
6088
  [[nodiscard]] bool ignore_case() const;
6089
6090
  // @see https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups
6091
  [[nodiscard]] bool has_regexp_groups() const;
6092
6093
  // Helper to test all components at once. Returns true if all match.
6094
  [[nodiscard]] bool test_components(
6095
      std::string_view protocol, std::string_view username,
6096
      std::string_view password, std::string_view hostname,
6097
      std::string_view port, std::string_view pathname, std::string_view search,
6098
      std::string_view hash) const;
6099
6100
#if ADA_TESTING
6101
  friend void PrintTo(const url_pattern& c, std::ostream* os) {
6102
    *os << "protocol_component: '" << c.get_protocol() << ", ";
6103
    *os << "username_component: '" << c.get_username() << ", ";
6104
    *os << "password_component: '" << c.get_password() << ", ";
6105
    *os << "hostname_component: '" << c.get_hostname() << ", ";
6106
    *os << "port_component: '" << c.get_port() << ", ";
6107
    *os << "pathname_component: '" << c.get_pathname() << ", ";
6108
    *os << "search_component: '" << c.get_search() << ", ";
6109
    *os << "hash_component: '" << c.get_hash();
6110
  }
6111
#endif  // ADA_TESTING
6112
6113
  template <url_pattern_regex::regex_concept P>
6114
  friend tl::expected<url_pattern<P>, errors> parser::parse_url_pattern_impl(
6115
      std::variant<std::string_view, url_pattern_init>&& input,
6116
      const std::string_view* base_url, const url_pattern_options* options);
6117
6118
  /**
6119
   * @private
6120
   * We can not make this private due to a LLVM bug.
6121
   * Ref: https://github.com/ada-url/ada/pull/859
6122
   */
6123
  url_pattern_component<regex_provider> protocol_component{};
6124
  /**
6125
   * @private
6126
   * We can not make this private due to a LLVM bug.
6127
   * Ref: https://github.com/ada-url/ada/pull/859
6128
   */
6129
  url_pattern_component<regex_provider> username_component{};
6130
  /**
6131
   * @private
6132
   * We can not make this private due to a LLVM bug.
6133
   * Ref: https://github.com/ada-url/ada/pull/859
6134
   */
6135
  url_pattern_component<regex_provider> password_component{};
6136
  /**
6137
   * @private
6138
   * We can not make this private due to a LLVM bug.
6139
   * Ref: https://github.com/ada-url/ada/pull/859
6140
   */
6141
  url_pattern_component<regex_provider> hostname_component{};
6142
  /**
6143
   * @private
6144
   * We can not make this private due to a LLVM bug.
6145
   * Ref: https://github.com/ada-url/ada/pull/859
6146
   */
6147
  url_pattern_component<regex_provider> port_component{};
6148
  /**
6149
   * @private
6150
   * We can not make this private due to a LLVM bug.
6151
   * Ref: https://github.com/ada-url/ada/pull/859
6152
   */
6153
  url_pattern_component<regex_provider> pathname_component{};
6154
  /**
6155
   * @private
6156
   * We can not make this private due to a LLVM bug.
6157
   * Ref: https://github.com/ada-url/ada/pull/859
6158
   */
6159
  url_pattern_component<regex_provider> search_component{};
6160
  /**
6161
   * @private
6162
   * We can not make this private due to a LLVM bug.
6163
   * Ref: https://github.com/ada-url/ada/pull/859
6164
   */
6165
  url_pattern_component<regex_provider> hash_component{};
6166
  /**
6167
   * @private
6168
   * We can not make this private due to a LLVM bug.
6169
   * Ref: https://github.com/ada-url/ada/pull/859
6170
   */
6171
  bool ignore_case_ = false;
6172
};
6173
}  // namespace ada
6174
#endif  // ADA_INCLUDE_URL_PATTERN
6175
#endif
6176
/* end file include/ada/url_pattern.h */
6177
/* begin file include/ada/url_pattern_helpers.h */
6178
/**
6179
 * @file url_pattern_helpers.h
6180
 * @brief Declaration for the URLPattern helpers.
6181
 */
6182
#ifndef ADA_URL_PATTERN_HELPERS_H
6183
#define ADA_URL_PATTERN_HELPERS_H
6184
6185
6186
#include <cstddef>
6187
#include <cstdint>
6188
#include <optional>
6189
#include <string>
6190
#include <string_view>
6191
#include <tuple>
6192
#include <unordered_set>
6193
#include <utility>
6194
#include <vector>
6195
6196
#if ADA_INCLUDE_URL_PATTERN
6197
namespace ada {
6198
enum class errors : uint8_t;
6199
}
6200
6201
namespace ada::url_pattern_helpers {
6202
6203
// @see https://urlpattern.spec.whatwg.org/#token
6204
enum class token_type : uint8_t {
6205
  INVALID_CHAR,    // 0
6206
  OPEN,            // 1
6207
  CLOSE,           // 2
6208
  REGEXP,          // 3
6209
  NAME,            // 4
6210
  CHAR,            // 5
6211
  ESCAPED_CHAR,    // 6
6212
  OTHER_MODIFIER,  // 7
6213
  ASTERISK,        // 8
6214
  END,             // 9
6215
};
6216
6217
#ifdef ADA_TESTING
6218
std::string to_string(token_type type);
6219
#endif  // ADA_TESTING
6220
6221
// @see https://urlpattern.spec.whatwg.org/#tokenize-policy
6222
enum class token_policy {
6223
  strict,
6224
  lenient,
6225
};
6226
6227
// @see https://urlpattern.spec.whatwg.org/#tokens
6228
class token {
6229
 public:
6230
  token(token_type _type, size_t _index, std::string_view _value)
6231
4.49M
      : type(_type), index(_index), value(_value) {}
6232
6233
  // A token has an associated type, a string, initially "invalid-char".
6234
  token_type type = token_type::INVALID_CHAR;
6235
6236
  // A token has an associated index, a number, initially 0. It is the position
6237
  // of the first code point in the pattern string represented by the token.
6238
  size_t index = 0;
6239
6240
  // A token has an associated value, a string, initially the empty string. It
6241
  // contains the code points from the pattern string represented by the token.
6242
  std::string_view value{};
6243
};
6244
6245
// @see https://urlpattern.spec.whatwg.org/#pattern-parser
6246
template <url_pattern_encoding_callback F>
6247
class url_pattern_parser {
6248
 public:
6249
  url_pattern_parser(F& encoding_callback_,
6250
                     std::string_view segment_wildcard_regexp_)
6251
1.11M
      : encoding_callback(encoding_callback_),
6252
1.11M
        segment_wildcard_regexp(segment_wildcard_regexp_) {}
6253
6254
4.35M
  bool can_continue() const { return index < tokens.size(); }
6255
6256
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-token
6257
  token* try_consume_token(token_type type);
6258
  // @see https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token
6259
  token* try_consume_modifier_token();
6260
  // @see
6261
  // https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token
6262
  token* try_consume_regexp_or_wildcard_token(const token* name_token);
6263
  // @see https://urlpattern.spec.whatwg.org/#consume-text
6264
  std::string consume_text();
6265
  // @see https://urlpattern.spec.whatwg.org/#consume-a-required-token
6266
  bool consume_required_token(token_type type);
6267
  // @see
6268
  // https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value
6269
  std::optional<errors> maybe_add_part_from_the_pending_fixed_value()
6270
      ada_warn_unused;
6271
  // @see https://urlpattern.spec.whatwg.org/#add-a-part
6272
  std::optional<errors> add_part(std::string_view prefix, token* name_token,
6273
                                 token* regexp_or_wildcard_token,
6274
                                 std::string_view suyffix,
6275
                                 token* modifier_token) ada_warn_unused;
6276
6277
  std::vector<token> tokens{};
6278
  F& encoding_callback;
6279
  std::string segment_wildcard_regexp;
6280
  std::vector<url_pattern_part> parts{};
6281
  std::string pending_fixed_value{};
6282
  size_t index = 0;
6283
  size_t next_numeric_name = 0;
6284
  // Names already taken by a part, for the "is a duplicate name" check in
6285
  // add_part. A linear scan of the part list there is O(n^2) over a pattern
6286
  // with n named parts (e.g. "/*" repeated); this set keeps it amortized O(1).
6287
  std::unordered_set<std::string> seen_names{};
6288
};
6289
6290
// @see https://urlpattern.spec.whatwg.org/#tokenizer
6291
class Tokenizer {
6292
 public:
6293
  explicit Tokenizer(std::string_view new_input, token_policy new_policy)
6294
1.20M
      : input(new_input), policy(new_policy) {}
6295
6296
  // @see https://urlpattern.spec.whatwg.org/#get-the-next-code-point
6297
  constexpr void get_next_code_point();
6298
6299
  // True when the most recent decoded unit was malformed UTF-8.
6300
3.38M
  bool had_invalid_code_point() const { return invalid_code_point; }
6301
6302
  // @see https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point
6303
  constexpr void seek_and_get_next_code_point(size_t index);
6304
6305
  // @see https://urlpattern.spec.whatwg.org/#add-a-token
6306
6307
  void add_token(token_type type, size_t next_position, size_t value_position,
6308
                 size_t value_length);
6309
6310
  // @see https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length
6311
  void add_token_with_default_length(token_type type, size_t next_position,
6312
                                     size_t value_position);
6313
6314
  // @see
6315
  // https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length
6316
  void add_token_with_defaults(token_type type);
6317
6318
  // @see https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error
6319
  std::optional<errors> process_tokenizing_error(
6320
      size_t next_position, size_t value_position) ada_warn_unused;
6321
6322
  friend tl::expected<std::vector<token>, errors> tokenize(
6323
      std::string_view input, token_policy policy);
6324
6325
 private:
6326
  // has an associated input, a pattern string, initially the empty string.
6327
  std::string_view input;
6328
  // has an associated policy, a tokenize policy, initially "strict".
6329
  token_policy policy;
6330
  // has an associated token list, a token list, initially an empty list.
6331
  std::vector<token> token_list{};
6332
  // has an associated index, a number, initially 0.
6333
  size_t index = 0;
6334
  // has an associated next index, a number, initially 0.
6335
  size_t next_index = 0;
6336
  // has an associated code point, a Unicode code point, initially null.
6337
  char32_t code_point{};
6338
  // Tracks whether the last decoded code point was malformed UTF-8.
6339
  bool invalid_code_point = false;
6340
};
6341
6342
// @see https://urlpattern.spec.whatwg.org/#constructor-string-parser
6343
template <url_pattern_regex::regex_concept regex_provider>
6344
struct constructor_string_parser {
6345
  explicit constructor_string_parser(std::string_view new_input,
6346
                                     std::vector<token>&& new_token_list)
6347
90.0k
      : input(new_input), token_list(std::move(new_token_list)) {}
6348
  // @see https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
6349
  static tl::expected<url_pattern_init, errors> parse(std::string_view input);
6350
6351
  // @see https://urlpattern.spec.whatwg.org/#constructor-string-parser-state
6352
  enum class State {
6353
    INIT,
6354
    PROTOCOL,
6355
    AUTHORITY,
6356
    USERNAME,
6357
    PASSWORD,
6358
    HOSTNAME,
6359
    PORT,
6360
    PATHNAME,
6361
    SEARCH,
6362
    HASH,
6363
    DONE,
6364
  };
6365
6366
  // @see
6367
  // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
6368
  std::optional<errors> compute_protocol_matches_special_scheme_flag();
6369
6370
 private:
6371
  // @see https://urlpattern.spec.whatwg.org/#rewind
6372
  constexpr void rewind();
6373
6374
  // @see https://urlpattern.spec.whatwg.org/#is-a-hash-prefix
6375
  constexpr bool is_hash_prefix();
6376
6377
  // @see https://urlpattern.spec.whatwg.org/#is-a-search-prefix
6378
  constexpr bool is_search_prefix();
6379
6380
  // @see https://urlpattern.spec.whatwg.org/#change-state
6381
  void change_state(State state, size_t skip);
6382
6383
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-open
6384
  constexpr bool is_group_open() const;
6385
6386
  // @see https://urlpattern.spec.whatwg.org/#is-a-group-close
6387
  constexpr bool is_group_close() const;
6388
6389
  // @see https://urlpattern.spec.whatwg.org/#is-a-protocol-suffix
6390
  constexpr bool is_protocol_suffix() const;
6391
6392
  // @see https://urlpattern.spec.whatwg.org/#next-is-authority-slashes
6393
  constexpr bool next_is_authority_slashes() const;
6394
6395
  // @see https://urlpattern.spec.whatwg.org/#is-an-identity-terminator
6396
  constexpr bool is_an_identity_terminator() const;
6397
6398
  // @see https://urlpattern.spec.whatwg.org/#is-a-pathname-start
6399
  constexpr bool is_pathname_start() const;
6400
6401
  // @see https://urlpattern.spec.whatwg.org/#is-a-password-prefix
6402
  constexpr bool is_password_prefix() const;
6403
6404
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-open
6405
  constexpr bool is_an_ipv6_open() const;
6406
6407
  // @see https://urlpattern.spec.whatwg.org/#is-an-ipv6-close
6408
  constexpr bool is_an_ipv6_close() const;
6409
6410
  // @see https://urlpattern.spec.whatwg.org/#is-a-port-prefix
6411
  constexpr bool is_port_prefix() const;
6412
6413
  // @see https://urlpattern.spec.whatwg.org/#is-a-non-special-pattern-char
6414
  constexpr bool is_non_special_pattern_char(size_t index,
6415
                                             uint32_t value) const;
6416
6417
  // @see https://urlpattern.spec.whatwg.org/#get-a-safe-token
6418
  constexpr const token* get_safe_token(size_t index) const;
6419
6420
  // @see https://urlpattern.spec.whatwg.org/#make-a-component-string
6421
  std::string make_component_string();
6422
  // has an associated input, a string, which must be set upon creation.
6423
  std::string_view input;
6424
  // has an associated token list, a token list, which must be set upon
6425
  // creation.
6426
  std::vector<token> token_list;
6427
  // has an associated result, a URLPatternInit, initially set to a new
6428
  // URLPatternInit.
6429
  url_pattern_init result{};
6430
  // has an associated component start, a number, initially set to 0.
6431
  size_t component_start = 0;
6432
  // has an associated token index, a number, initially set to 0.
6433
  size_t token_index = 0;
6434
  // has an associated token increment, a number, initially set to 1.
6435
  size_t token_increment = 1;
6436
  // has an associated group depth, a number, initially set to 0.
6437
  size_t group_depth = 0;
6438
  // has an associated hostname IPv6 bracket depth, a number, initially set to
6439
  // 0.
6440
  size_t hostname_ipv6_bracket_depth = 0;
6441
  // has an associated protocol matches a special scheme flag, a boolean,
6442
  // initially set to false.
6443
  bool protocol_matches_a_special_scheme_flag = false;
6444
  // has an associated state, a string, initially set to "init".
6445
  State state = State::INIT;
6446
};
6447
6448
// @see https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol
6449
tl::expected<std::string, errors> canonicalize_protocol(std::string_view input);
6450
6451
// @see https://wicg.github.io/urlpattern/#canonicalize-a-username
6452
tl::expected<std::string, errors> canonicalize_username(std::string_view input);
6453
6454
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6455
tl::expected<std::string, errors> canonicalize_password(std::string_view input);
6456
6457
// @see https://wicg.github.io/urlpattern/#canonicalize-a-password
6458
tl::expected<std::string, errors> canonicalize_hostname(std::string_view input);
6459
6460
// @see https://wicg.github.io/urlpattern/#canonicalize-an-ipv6-hostname
6461
tl::expected<std::string, errors> canonicalize_ipv6_hostname(
6462
    std::string_view input);
6463
6464
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6465
tl::expected<std::string, errors> canonicalize_port(std::string_view input);
6466
6467
// @see https://wicg.github.io/urlpattern/#canonicalize-a-port
6468
tl::expected<std::string, errors> canonicalize_port_with_protocol(
6469
    std::string_view input, std::string_view protocol);
6470
6471
// @see https://wicg.github.io/urlpattern/#canonicalize-a-pathname
6472
tl::expected<std::string, errors> canonicalize_pathname(std::string_view input);
6473
6474
// @see https://wicg.github.io/urlpattern/#canonicalize-an-opaque-pathname
6475
tl::expected<std::string, errors> canonicalize_opaque_pathname(
6476
    std::string_view input);
6477
6478
// @see https://wicg.github.io/urlpattern/#canonicalize-a-search
6479
tl::expected<std::string, errors> canonicalize_search(std::string_view input);
6480
6481
// @see https://wicg.github.io/urlpattern/#canonicalize-a-hash
6482
tl::expected<std::string, errors> canonicalize_hash(std::string_view input);
6483
6484
// @see https://urlpattern.spec.whatwg.org/#tokenize
6485
tl::expected<std::vector<token>, errors> tokenize(std::string_view input,
6486
                                                  token_policy policy);
6487
6488
// @see https://urlpattern.spec.whatwg.org/#process-a-base-url-string
6489
std::string process_base_url_string(std::string_view input,
6490
                                    url_pattern_init::process_type type);
6491
6492
// @see https://urlpattern.spec.whatwg.org/#escape-a-pattern-string
6493
std::string escape_pattern_string(std::string_view input);
6494
6495
// @see https://urlpattern.spec.whatwg.org/#escape-a-regexp-string
6496
std::string escape_regexp_string(std::string_view input);
6497
6498
// @see https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname
6499
constexpr bool is_absolute_pathname(
6500
    std::string_view input, url_pattern_init::process_type type) noexcept;
6501
6502
// @see https://urlpattern.spec.whatwg.org/#parse-a-pattern-string
6503
template <url_pattern_encoding_callback F>
6504
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
6505
    std::string_view input, url_pattern_compile_component_options& options,
6506
    F& encoding_callback);
6507
6508
// @see https://urlpattern.spec.whatwg.org/#generate-a-pattern-string
6509
std::string generate_pattern_string(
6510
    std::vector<url_pattern_part>& part_list,
6511
    url_pattern_compile_component_options& options);
6512
6513
// @see
6514
// https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list
6515
std::tuple<std::string, std::vector<std::string>>
6516
generate_regular_expression_and_name_list(
6517
    const std::vector<url_pattern_part>& part_list,
6518
    url_pattern_compile_component_options options);
6519
6520
// @see https://urlpattern.spec.whatwg.org/#hostname-pattern-is-an-ipv6-address
6521
bool is_ipv6_address(std::string_view input) noexcept;
6522
6523
// @see
6524
// https://urlpattern.spec.whatwg.org/#protocol-component-matches-a-special-scheme
6525
template <url_pattern_regex::regex_concept regex_provider>
6526
bool protocol_component_matches_special_scheme(
6527
    ada::url_pattern_component<regex_provider>& input);
6528
6529
// @see https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string
6530
std::string_view convert_modifier_to_string(url_pattern_part_modifier modifier);
6531
6532
// @see https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
6533
std::string generate_segment_wildcard_regexp(
6534
    url_pattern_compile_component_options options);
6535
6536
}  // namespace ada::url_pattern_helpers
6537
#endif  // ADA_INCLUDE_URL_PATTERN
6538
#endif
6539
/* end file include/ada/url_pattern_helpers.h */
6540
6541
#include <cstdint>
6542
#include <string>
6543
#include <string_view>
6544
#include <utility>
6545
#include <variant>
6546
6547
namespace ada::parser {
6548
#if ADA_INCLUDE_URL_PATTERN
6549
template <url_pattern_regex::regex_concept regex_provider>
6550
tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
6551
    std::variant<std::string_view, url_pattern_init>&& input,
6552
240k
    const std::string_view* base_url, const url_pattern_options* options) {
6553
  // Let init be null.
6554
240k
  url_pattern_init init;
6555
6556
  // If input is a scalar value string then:
6557
240k
  if (std::holds_alternative<std::string_view>(input)) {
6558
    // Set init to the result of running parse a constructor string given input.
6559
90.0k
    auto parse_result =
6560
90.0k
        url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
6561
90.0k
            std::get<std::string_view>(input));
6562
90.0k
    if (!parse_result) {
6563
1.38k
      ada_log("constructor_string_parser::parse failed");
6564
1.38k
      return tl::unexpected(parse_result.error());
6565
1.38k
    }
6566
88.6k
    init = std::move(*parse_result);
6567
    // If baseURL is null and init["protocol"] does not exist, then throw a
6568
    // TypeError.
6569
88.6k
    if (!base_url && !init.protocol) {
6570
14.5k
      ada_log("base url is null and protocol is not set");
6571
14.5k
      return tl::unexpected(errors::type_error);
6572
14.5k
    }
6573
6574
    // If baseURL is not null, set init["baseURL"] to baseURL.
6575
74.1k
    if (base_url) {
6576
59.1k
      init.base_url = std::string(*base_url);
6577
59.1k
    }
6578
150k
  } else {
6579
    // Assert: input is a URLPatternInit.
6580
150k
    ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
6581
    // If baseURL is not null, then throw a TypeError.
6582
150k
    if (base_url) {
6583
30.0k
      ada_log("base url is not null");
6584
30.0k
      return tl::unexpected(errors::type_error);
6585
30.0k
    }
6586
    // Optimization: Avoid copy by moving the input value.
6587
    // Set init to input.
6588
120k
    init = std::move(std::get<url_pattern_init>(input));
6589
120k
  }
6590
6591
  // Let processedInit be the result of process a URLPatternInit given init,
6592
  // "pattern", null, null, null, null, null, null, null, and null.
6593
194k
  auto processed_init =
6594
194k
      url_pattern_init::process(init, url_pattern_init::process_type::pattern);
6595
194k
  if (!processed_init) {
6596
57.6k
    ada_log("url_pattern_init::process failed for init and 'pattern'");
6597
57.6k
    return tl::unexpected(processed_init.error());
6598
57.6k
  }
6599
6600
  // For each componentName of  "protocol", "username", "password", "hostname",
6601
  // "port", "pathname", "search", "hash" If processedInit[componentName] does
6602
  // not exist, then set processedInit[componentName] to "*".
6603
136k
  ADA_ASSERT_TRUE(processed_init.has_value());
6604
136k
  if (!processed_init->protocol) processed_init->protocol = "*";
6605
136k
  if (!processed_init->username) processed_init->username = "*";
6606
136k
  if (!processed_init->password) processed_init->password = "*";
6607
136k
  if (!processed_init->hostname) processed_init->hostname = "*";
6608
136k
  if (!processed_init->port) processed_init->port = "*";
6609
136k
  if (!processed_init->pathname) processed_init->pathname = "*";
6610
136k
  if (!processed_init->search) processed_init->search = "*";
6611
136k
  if (!processed_init->hash) processed_init->hash = "*";
6612
6613
136k
  ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
6614
136k
  ada_log("-- processed_init->username: ", processed_init->username.value());
6615
136k
  ada_log("-- processed_init->password: ", processed_init->password.value());
6616
136k
  ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
6617
136k
  ada_log("-- processed_init->port: ", processed_init->port.value());
6618
136k
  ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
6619
136k
  ada_log("-- processed_init->search: ", processed_init->search.value());
6620
136k
  ada_log("-- processed_init->hash: ", processed_init->hash.value());
6621
6622
  // If processedInit["protocol"] is a special scheme and processedInit["port"]
6623
  // is a string which represents its corresponding default port in radix-10
6624
  // using ASCII digits then set processedInit["port"] to the empty string.
6625
  // TODO: Optimization opportunity.
6626
136k
  if (scheme::is_special(*processed_init->protocol)) {
6627
    // file is the only special scheme with no default port; get_special_port
6628
    // returns 0 as its sentinel, so a literal "0" port must not be mistaken for
6629
    // a default and dropped.
6630
76.5k
    uint16_t default_port = scheme::get_special_port(*processed_init->protocol);
6631
76.5k
    if (default_port != 0 &&
6632
76.5k
        std::to_string(default_port) == processed_init->port.value()) {
6633
3
      processed_init->port->clear();
6634
3
    }
6635
76.5k
  }
6636
6637
  // Let urlPattern be a new URL pattern.
6638
136k
  url_pattern<regex_provider> url_pattern_{};
6639
6640
  // Set urlPattern's protocol component to the result of compiling a component
6641
  // given processedInit["protocol"], canonicalize a protocol, and default
6642
  // options.
6643
136k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
6644
136k
      processed_init->protocol.value(),
6645
136k
      url_pattern_helpers::canonicalize_protocol,
6646
136k
      url_pattern_compile_component_options::DEFAULT);
6647
136k
  if (!protocol_component) {
6648
1.77k
    ada_log("url_pattern_component::compile failed for protocol ",
6649
1.77k
            processed_init->protocol.value());
6650
1.77k
    return tl::unexpected(protocol_component.error());
6651
1.77k
  }
6652
134k
  url_pattern_.protocol_component = std::move(*protocol_component);
6653
6654
  // Set urlPattern's username component to the result of compiling a component
6655
  // given processedInit["username"], canonicalize a username, and default
6656
  // options.
6657
134k
  auto username_component = url_pattern_component<regex_provider>::compile(
6658
134k
      processed_init->username.value(),
6659
134k
      url_pattern_helpers::canonicalize_username,
6660
134k
      url_pattern_compile_component_options::DEFAULT);
6661
134k
  if (!username_component) {
6662
390
    ada_log("url_pattern_component::compile failed for username ",
6663
390
            processed_init->username.value());
6664
390
    return tl::unexpected(username_component.error());
6665
390
  }
6666
134k
  url_pattern_.username_component = std::move(*username_component);
6667
6668
  // Set urlPattern's password component to the result of compiling a component
6669
  // given processedInit["password"], canonicalize a password, and default
6670
  // options.
6671
134k
  auto password_component = url_pattern_component<regex_provider>::compile(
6672
134k
      processed_init->password.value(),
6673
134k
      url_pattern_helpers::canonicalize_password,
6674
134k
      url_pattern_compile_component_options::DEFAULT);
6675
134k
  if (!password_component) {
6676
124
    ada_log("url_pattern_component::compile failed for password ",
6677
124
            processed_init->password.value());
6678
124
    return tl::unexpected(password_component.error());
6679
124
  }
6680
134k
  url_pattern_.password_component = std::move(*password_component);
6681
6682
  // TODO: Optimization opportunity. The following if statement can be
6683
  // simplified.
6684
  // If the result running hostname pattern is an IPv6 address given
6685
  // processedInit["hostname"] is true, then set urlPattern's hostname component
6686
  // to the result of compiling a component given processedInit["hostname"],
6687
  // canonicalize an IPv6 hostname, and hostname options.
6688
134k
  if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
6689
186
    ada_log("processed_init->hostname is ipv6 address");
6690
    // then set urlPattern's hostname component to the result of compiling a
6691
    // component given processedInit["hostname"], canonicalize an IPv6 hostname,
6692
    // and hostname options.
6693
186
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6694
186
        processed_init->hostname.value(),
6695
186
        url_pattern_helpers::canonicalize_ipv6_hostname,
6696
186
        url_pattern_compile_component_options::DEFAULT);
6697
186
    if (!hostname_component) {
6698
40
      ada_log("url_pattern_component::compile failed for ipv6 hostname ",
6699
40
              processed_init->hostname.value());
6700
40
      return tl::unexpected(hostname_component.error());
6701
40
    }
6702
146
    url_pattern_.hostname_component = std::move(*hostname_component);
6703
134k
  } else {
6704
    // Otherwise, set urlPattern's hostname component to the result of compiling
6705
    // a component given processedInit["hostname"], canonicalize a hostname, and
6706
    // hostname options.
6707
134k
    auto hostname_component = url_pattern_component<regex_provider>::compile(
6708
134k
        processed_init->hostname.value(),
6709
134k
        url_pattern_helpers::canonicalize_hostname,
6710
134k
        url_pattern_compile_component_options::HOSTNAME);
6711
134k
    if (!hostname_component) {
6712
1.72k
      ada_log("url_pattern_component::compile failed for hostname ",
6713
1.72k
              processed_init->hostname.value());
6714
1.72k
      return tl::unexpected(hostname_component.error());
6715
1.72k
    }
6716
132k
    url_pattern_.hostname_component = std::move(*hostname_component);
6717
132k
  }
6718
6719
  // Set urlPattern's port component to the result of compiling a component
6720
  // given processedInit["port"], canonicalize a port, and default options.
6721
132k
  auto port_component = url_pattern_component<regex_provider>::compile(
6722
132k
      processed_init->port.value(), url_pattern_helpers::canonicalize_port,
6723
132k
      url_pattern_compile_component_options::DEFAULT);
6724
132k
  if (!port_component) {
6725
661
    ada_log("url_pattern_component::compile failed for port ",
6726
661
            processed_init->port.value());
6727
661
    return tl::unexpected(port_component.error());
6728
661
  }
6729
131k
  url_pattern_.port_component = std::move(*port_component);
6730
6731
  // Let compileOptions be a copy of the default options with the ignore case
6732
  // property set to options["ignoreCase"].
6733
131k
  auto compile_options = url_pattern_compile_component_options::DEFAULT;
6734
131k
  if (options) {
6735
15.0k
    compile_options.ignore_case = options->ignore_case;
6736
    // Recorded on the pattern so that url_pattern::ignore_case() reports the
6737
    // option it was created with.
6738
15.0k
    url_pattern_.ignore_case_ = options->ignore_case;
6739
15.0k
  }
6740
6741
  // TODO: Optimization opportunity: Simplify this if statement.
6742
  // If the result of running protocol component matches a special scheme given
6743
  // urlPattern's protocol component is true, then:
6744
131k
  if (url_pattern_helpers::protocol_component_matches_special_scheme<
6745
131k
          regex_provider>(url_pattern_.protocol_component)) {
6746
    // Let pathCompileOptions be copy of the pathname options with the ignore
6747
    // case property set to options["ignoreCase"].
6748
106k
    auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
6749
106k
    if (options) {
6750
15.0k
      path_compile_options.ignore_case = options->ignore_case;
6751
15.0k
    }
6752
6753
    // Set urlPattern's pathname component to the result of compiling a
6754
    // component given processedInit["pathname"], canonicalize a pathname, and
6755
    // pathCompileOptions.
6756
106k
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6757
106k
        processed_init->pathname.value(),
6758
106k
        url_pattern_helpers::canonicalize_pathname, path_compile_options);
6759
106k
    if (!pathname_component) {
6760
1.64k
      ada_log("url_pattern_component::compile failed for pathname ",
6761
1.64k
              processed_init->pathname.value());
6762
1.64k
      return tl::unexpected(pathname_component.error());
6763
1.64k
    }
6764
105k
    url_pattern_.pathname_component = std::move(*pathname_component);
6765
105k
  } else {
6766
    // Otherwise set urlPattern's pathname component to the result of compiling
6767
    // a component given processedInit["pathname"], canonicalize an opaque
6768
    // pathname, and compileOptions.
6769
24.9k
    auto pathname_component = url_pattern_component<regex_provider>::compile(
6770
24.9k
        processed_init->pathname.value(),
6771
24.9k
        url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
6772
24.9k
    if (!pathname_component) {
6773
365
      ada_log("url_pattern_component::compile failed for opaque pathname ",
6774
365
              processed_init->pathname.value());
6775
365
      return tl::unexpected(pathname_component.error());
6776
365
    }
6777
24.5k
    url_pattern_.pathname_component = std::move(*pathname_component);
6778
24.5k
  }
6779
6780
  // Set urlPattern's search component to the result of compiling a component
6781
  // given processedInit["search"], canonicalize a search, and compileOptions.
6782
129k
  auto search_component = url_pattern_component<regex_provider>::compile(
6783
129k
      processed_init->search.value(), url_pattern_helpers::canonicalize_search,
6784
129k
      compile_options);
6785
129k
  if (!search_component) {
6786
89
    ada_log("url_pattern_component::compile failed for search ",
6787
89
            processed_init->search.value());
6788
89
    return tl::unexpected(search_component.error());
6789
89
  }
6790
129k
  url_pattern_.search_component = std::move(*search_component);
6791
6792
  // Set urlPattern's hash component to the result of compiling a component
6793
  // given processedInit["hash"], canonicalize a hash, and compileOptions.
6794
129k
  auto hash_component = url_pattern_component<regex_provider>::compile(
6795
129k
      processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
6796
129k
      compile_options);
6797
129k
  if (!hash_component) {
6798
93
    ada_log("url_pattern_component::compile failed for hash ",
6799
93
            processed_init->hash.value());
6800
93
    return tl::unexpected(hash_component.error());
6801
93
  }
6802
129k
  url_pattern_.hash_component = std::move(*hash_component);
6803
6804
  // Return urlPattern.
6805
129k
  return url_pattern_;
6806
129k
}
6807
#endif  // ADA_INCLUDE_URL_PATTERN
6808
6809
}  // namespace ada::parser
6810
6811
#endif  // ADA_PARSER_INL_H
6812
/* end file include/ada/parser-inl.h */
6813
/* begin file include/ada/scheme-inl.h */
6814
/**
6815
 * @file scheme-inl.h
6816
 * @brief Definitions for the URL scheme.
6817
 */
6818
#ifndef ADA_SCHEME_INL_H
6819
#define ADA_SCHEME_INL_H
6820
6821
6822
#include <cstddef>
6823
#include <cstdint>
6824
#include <string_view>
6825
6826
namespace ada::scheme {
6827
6828
/**
6829
 * @namespace ada::scheme::details
6830
 * @brief Includes the definitions for scheme specific entities
6831
 */
6832
namespace details {
6833
// for use with is_special and get_special_port
6834
// Spaces, if present, are removed from URL.
6835
constexpr std::string_view is_special_list[] = {"http", " ",   "https", "ws",
6836
                                                "ftp",  "wss", "file",  " "};
6837
// for use with get_special_port
6838
constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0};
6839
6840
// @private
6841
// convert a string_view to a 64-bit integer key for fast comparison
6842
0
constexpr uint64_t make_key(std::string_view sv) {
6843
0
  uint64_t val = 0;
6844
0
  for (size_t i = 0; i < sv.size(); i++)
6845
0
    val |= (uint64_t)(uint8_t)sv[i] << (i * 8);
6846
0
  return val;
6847
0
}
6848
// precomputed keys for the special schemes, indexed by a hash of the input
6849
// string
6850
constexpr uint64_t scheme_keys[] = {
6851
    make_key("http"),   // 0: HTTP
6852
    0,                  // 1: sentinel
6853
    make_key("https"),  // 2: HTTPS
6854
    make_key("ws"),     // 3: WS
6855
    make_key("ftp"),    // 4: FTP
6856
    make_key("wss"),    // 5: WSS
6857
    make_key("file"),   // 6: FILE
6858
    0,                  // 7: sentinel
6859
};
6860
6861
// @private
6862
// branchless load of up to 5 characters into a uint64_t, padding with zeros if
6863
// n < 5
6864
862k
inline uint64_t branchless_load5(const char* p, size_t n) {
6865
862k
  uint64_t input = (uint8_t)p[0];
6866
862k
  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
6867
862k
  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
6868
862k
  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
6869
862k
  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
6870
862k
  return input;
6871
862k
}
6872
}  // namespace details
6873
6874
/****
6875
 * @private
6876
 * In is_special, get_scheme_type, and get_special_port, we
6877
 * use a standard hashing technique to find the index of the scheme in
6878
 * the is_special_list. The hashing technique is based on the size of
6879
 * the scheme and the first character of the scheme. It ensures that we
6880
 * do at most one string comparison per call. If the protocol is
6881
 * predictible (e.g., it is always "http"), we can get a better average
6882
 * performance by using a simpler approach where we loop and compare
6883
 * scheme with all possible protocols starting with the most likely
6884
 * protocol. Doing multiple comparisons may have a poor worst case
6885
 * performance, however. In this instance, we choose a potentially
6886
 * slightly lower best-case performance for a better worst-case
6887
 * performance. We can revisit this choice at any time.
6888
 *
6889
 * Reference:
6890
 * Schmidt, Douglas C. "Gperf: A perfect hash function generator."
6891
 * More C++ gems 17 (2000).
6892
 *
6893
 * Reference: https://en.wikipedia.org/wiki/Perfect_hash_function
6894
 *
6895
 * Reference: https://github.com/ada-url/ada/issues/617
6896
 ****/
6897
6898
261k
ada_really_inline constexpr bool is_special(std::string_view scheme) {
6899
261k
  if (scheme.empty()) {
6900
26.1k
    return false;
6901
26.1k
  }
6902
235k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6903
235k
  const std::string_view target = details::is_special_list[hash_value];
6904
235k
  return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1));
6905
261k
}
6906
76.5k
constexpr uint16_t get_special_port(std::string_view scheme) noexcept {
6907
76.5k
  if (scheme.empty()) {
6908
0
    return 0;
6909
0
  }
6910
76.5k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6911
76.5k
  const std::string_view target = details::is_special_list[hash_value];
6912
76.5k
  if (scheme.size() == target.size() &&
6913
76.5k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6914
76.5k
          details::scheme_keys[hash_value]) {
6915
76.5k
    return details::special_ports[hash_value];
6916
76.5k
  } else {
6917
0
    return 0;
6918
0
  }
6919
76.5k
}
6920
47.2k
constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
6921
47.2k
  return details::special_ports[int(type)];
6922
47.2k
}
6923
785k
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
6924
785k
  if (scheme.empty()) {
6925
0
    return ada::scheme::NOT_SPECIAL;
6926
0
  }
6927
785k
  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
6928
785k
  const std::string_view target = details::is_special_list[hash_value];
6929
785k
  if (scheme.size() == target.size() &&
6930
785k
      details::branchless_load5(scheme.data(), scheme.size()) ==
6931
785k
          details::scheme_keys[hash_value]) {
6932
734k
    return ada::scheme::type(hash_value);
6933
734k
  } else {
6934
51.4k
    return ada::scheme::NOT_SPECIAL;
6935
51.4k
  }
6936
785k
}
6937
6938
}  // namespace ada::scheme
6939
6940
#endif  // ADA_SCHEME_INL_H
6941
/* end file include/ada/scheme-inl.h */
6942
/* begin file include/ada/serializers.h */
6943
/**
6944
 * @file serializers.h
6945
 * @brief IP address serialization utilities.
6946
 *
6947
 * This header provides functions for converting IP addresses to their
6948
 * string representations according to the WHATWG URL Standard.
6949
 */
6950
#ifndef ADA_SERIALIZERS_H
6951
#define ADA_SERIALIZERS_H
6952
6953
6954
#include <array>
6955
#include <cstddef>
6956
#include <cstdint>
6957
#include <string>
6958
6959
/**
6960
 * @namespace ada::serializers
6961
 * @brief IP address serialization functions.
6962
 *
6963
 * Contains utilities for serializing IPv4 and IPv6 addresses to strings.
6964
 */
6965
namespace ada::serializers {
6966
6967
/**
6968
 * Finds the longest consecutive sequence of zero pieces in an IPv6 address.
6969
 * Used for :: compression in IPv6 serialization.
6970
 *
6971
 * @param address The 8 16-bit pieces of the IPv6 address.
6972
 * @param[out] compress Index of the start of the longest zero sequence.
6973
 * @param[out] compress_length Length of the longest zero sequence.
6974
 */
6975
void find_longest_sequence_of_ipv6_pieces(
6976
    const std::array<uint16_t, 8>& address, size_t& compress,
6977
    size_t& compress_length) noexcept;
6978
6979
/**
6980
 * Serializes an IPv6 address to its string representation.
6981
 *
6982
 * @param address The 8 16-bit pieces of the IPv6 address.
6983
 * @return The serialized IPv6 string (e.g., "2001:db8::1").
6984
 * @see https://url.spec.whatwg.org/#concept-ipv6-serializer
6985
 */
6986
std::string ipv6(const std::array<uint16_t, 8>& address);
6987
6988
/**
6989
 * Serializes an IPv4 address to its dotted-decimal string representation.
6990
 *
6991
 * @param address The 32-bit IPv4 address as an integer.
6992
 * @return The serialized IPv4 string (e.g., "192.168.1.1").
6993
 * @see https://url.spec.whatwg.org/#concept-ipv4-serializer
6994
 */
6995
std::string ipv4(uint64_t address);
6996
6997
}  // namespace ada::serializers
6998
6999
#endif  // ADA_SERIALIZERS_H
7000
/* end file include/ada/serializers.h */
7001
/* begin file include/ada/state.h */
7002
/**
7003
 * @file state.h
7004
 * @brief URL parser state machine states.
7005
 *
7006
 * Defines the states used by the URL parsing state machine as specified
7007
 * in the WHATWG URL Standard.
7008
 *
7009
 * @see https://url.spec.whatwg.org/#url-parsing
7010
 */
7011
#ifndef ADA_STATE_H
7012
#define ADA_STATE_H
7013
7014
7015
#include <string>
7016
7017
namespace ada {
7018
7019
/**
7020
 * @brief States in the URL parsing state machine.
7021
 *
7022
 * The URL parser processes input through a sequence of states, each handling
7023
 * a specific part of the URL syntax.
7024
 *
7025
 * @see https://url.spec.whatwg.org/#url-parsing
7026
 */
7027
enum class state {
7028
  /**
7029
   * @see https://url.spec.whatwg.org/#authority-state
7030
   */
7031
  AUTHORITY,
7032
7033
  /**
7034
   * @see https://url.spec.whatwg.org/#scheme-start-state
7035
   */
7036
  SCHEME_START,
7037
7038
  /**
7039
   * @see https://url.spec.whatwg.org/#scheme-state
7040
   */
7041
  SCHEME,
7042
7043
  /**
7044
   * @see https://url.spec.whatwg.org/#host-state
7045
   */
7046
  HOST,
7047
7048
  /**
7049
   * @see https://url.spec.whatwg.org/#no-scheme-state
7050
   */
7051
  NO_SCHEME,
7052
7053
  /**
7054
   * @see https://url.spec.whatwg.org/#fragment-state
7055
   */
7056
  FRAGMENT,
7057
7058
  /**
7059
   * @see https://url.spec.whatwg.org/#relative-state
7060
   */
7061
  RELATIVE_SCHEME,
7062
7063
  /**
7064
   * @see https://url.spec.whatwg.org/#relative-slash-state
7065
   */
7066
  RELATIVE_SLASH,
7067
7068
  /**
7069
   * @see https://url.spec.whatwg.org/#file-state
7070
   */
7071
  FILE,
7072
7073
  /**
7074
   * @see https://url.spec.whatwg.org/#file-host-state
7075
   */
7076
  FILE_HOST,
7077
7078
  /**
7079
   * @see https://url.spec.whatwg.org/#file-slash-state
7080
   */
7081
  FILE_SLASH,
7082
7083
  /**
7084
   * @see https://url.spec.whatwg.org/#path-or-authority-state
7085
   */
7086
  PATH_OR_AUTHORITY,
7087
7088
  /**
7089
   * @see https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
7090
   */
7091
  SPECIAL_AUTHORITY_IGNORE_SLASHES,
7092
7093
  /**
7094
   * @see https://url.spec.whatwg.org/#special-authority-slashes-state
7095
   */
7096
  SPECIAL_AUTHORITY_SLASHES,
7097
7098
  /**
7099
   * @see https://url.spec.whatwg.org/#special-relative-or-authority-state
7100
   */
7101
  SPECIAL_RELATIVE_OR_AUTHORITY,
7102
7103
  /**
7104
   * @see https://url.spec.whatwg.org/#query-state
7105
   */
7106
  QUERY,
7107
7108
  /**
7109
   * @see https://url.spec.whatwg.org/#path-state
7110
   */
7111
  PATH,
7112
7113
  /**
7114
   * @see https://url.spec.whatwg.org/#path-start-state
7115
   */
7116
  PATH_START,
7117
7118
  /**
7119
   * @see https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
7120
   */
7121
  OPAQUE_PATH,
7122
7123
  /**
7124
   * @see https://url.spec.whatwg.org/#port-state
7125
   */
7126
  PORT,
7127
};
7128
7129
/**
7130
 * Converts a parser state to its string name for debugging.
7131
 * @param s The state to convert.
7132
 * @return A string representation of the state.
7133
 */
7134
ada_warn_unused std::string to_string(ada::state s);
7135
7136
}  // namespace ada
7137
7138
#endif  // ADA_STATE_H
7139
/* end file include/ada/state.h */
7140
/* begin file include/ada/unicode.h */
7141
/**
7142
 * @file unicode.h
7143
 * @brief Definitions for all unicode specific functions.
7144
 */
7145
#ifndef ADA_UNICODE_H
7146
#define ADA_UNICODE_H
7147
7148
7149
#include <cstddef>
7150
#include <cstdint>
7151
#include <optional>
7152
#include <string>
7153
#include <string_view>
7154
7155
/**
7156
 * Unicode operations. These functions are not part of our public API and may
7157
 * change at any time.
7158
 *
7159
 * @private
7160
 * @namespace ada::unicode
7161
 * @brief Includes the definitions for unicode operations
7162
 */
7163
namespace ada::unicode {
7164
7165
/**
7166
 * @private
7167
 * We receive a UTF-8 string representing a domain name.
7168
 * If the string is percent encoded, we apply percent decoding.
7169
 *
7170
 * Given a domain, we need to identify its labels.
7171
 * They are separated by label-separators:
7172
 *
7173
 * U+002E (.) FULL STOP
7174
 * U+FF0E FULLWIDTH FULL STOP
7175
 * U+3002 IDEOGRAPHIC FULL STOP
7176
 * U+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
7177
 *
7178
 * They are all mapped to U+002E.
7179
 *
7180
 * We process each label into a string that should not exceed 63 octets.
7181
 * If the string is already punycode (starts with "xn--"), then we must
7182
 * scan it to look for unallowed code points.
7183
 * Otherwise, if the string is not pure ASCII, we need to transcode it
7184
 * to punycode by following RFC 3454 which requires us to
7185
 * - Map characters  (see section 3),
7186
 * - Normalize (see section 4),
7187
 * - Reject forbidden characters,
7188
 * - Check for right-to-left characters and if so, check all requirements (see
7189
 * section 6),
7190
 * - Optionally reject based on unassigned code points (section 7).
7191
 *
7192
 * The Unicode standard provides a table of code points with a mapping, a list
7193
 * of forbidden code points and so forth. This table is subject to change and
7194
 * will vary based on the implementation. For Unicode 15, the table is at
7195
 * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt
7196
 * If you use ICU, they parse this table and map it to code using a Python
7197
 * script.
7198
 *
7199
 * The resulting strings should not exceed 255 octets according to RFC 1035
7200
 * section 2.3.4. ICU checks for label size and domain size, but these errors
7201
 * are ignored.
7202
 *
7203
 * @see https://url.spec.whatwg.org/#concept-domain-to-ascii
7204
 *
7205
 */
7206
bool to_ascii(std::optional<std::string>& out, std::string_view plain,
7207
              size_t first_percent);
7208
7209
/**
7210
 * @private
7211
 * Checks if the input has tab or newline characters.
7212
 *
7213
 * @attention The has_tabs_or_newline function is a bottleneck and it is simple
7214
 * enough that compilers like GCC can 'autovectorize it'.
7215
 */
7216
ada_really_inline bool has_tabs_or_newline(
7217
    std::string_view user_input) noexcept;
7218
7219
/**
7220
 * @private
7221
 * Checks if the input is a forbidden host code point.
7222
 * @see https://url.spec.whatwg.org/#forbidden-host-code-point
7223
 */
7224
ada_really_inline constexpr bool is_forbidden_host_code_point(char c) noexcept;
7225
7226
/**
7227
 * @private
7228
 * Checks if the input contains a forbidden domain code point.
7229
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7230
 */
7231
ada_really_inline constexpr bool contains_forbidden_domain_code_point(
7232
    const char* input, size_t length) noexcept;
7233
7234
/**
7235
 * @private
7236
 * Checks if the input contains a forbidden domain code point in which case
7237
 * the first bit is set to 1. If the input contains an upper case ASCII letter,
7238
 * then the second bit is set to 1.
7239
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7240
 */
7241
ada_really_inline constexpr uint8_t
7242
contains_forbidden_domain_code_point_or_upper(const char* input,
7243
                                              size_t length) noexcept;
7244
7245
/**
7246
 * @private
7247
 * Checks if the input is a forbidden domain code point.
7248
 * @see https://url.spec.whatwg.org/#forbidden-domain-code-point
7249
 */
7250
ada_really_inline constexpr bool is_forbidden_domain_code_point(
7251
    char c) noexcept;
7252
7253
/**
7254
 * @private
7255
 * Checks if the input is alphanumeric, '+', '-' or '.'
7256
 */
7257
ada_really_inline constexpr bool is_alnum_plus(char c) noexcept;
7258
7259
/**
7260
 * @private
7261
 * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex
7262
 * digit. An ASCII upper hex digit is an ASCII digit or a code point in the
7263
 * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an
7264
 * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive.
7265
 */
7266
ada_really_inline constexpr bool is_ascii_hex_digit(char c) noexcept;
7267
7268
/**
7269
 * @private
7270
 * An ASCII digit is a code point in the range U+0030 (0) to U+0039 (9),
7271
 * inclusive.
7272
 */
7273
ada_really_inline constexpr bool is_ascii_digit(char c) noexcept;
7274
7275
/**
7276
 * @private
7277
 * @details If a char is between U+0000 and U+007F inclusive, then it's an ASCII
7278
 * character.
7279
 */
7280
ada_really_inline constexpr bool is_ascii(char32_t c) noexcept;
7281
7282
/**
7283
 * @private
7284
 * Checks if the input is a C0 control or space character.
7285
 *
7286
 * @details A C0 control or space is a C0 control or U+0020 SPACE.
7287
 * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION
7288
 * SEPARATOR ONE, inclusive.
7289
 */
7290
ada_really_inline constexpr bool is_c0_control_or_space(char c) noexcept;
7291
7292
/**
7293
 * @private
7294
 * Checks if the input is a ASCII tab or newline character.
7295
 *
7296
 * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR.
7297
 */
7298
ada_really_inline constexpr bool is_ascii_tab_or_newline(char c) noexcept;
7299
7300
/**
7301
 * @private
7302
 * @details A double-dot path segment must be ".." or an ASCII case-insensitive
7303
 * match for ".%2e", "%2e.", or "%2e%2e".
7304
 */
7305
ada_really_inline constexpr bool is_double_dot_path_segment(
7306
    std::string_view input) noexcept;
7307
7308
/**
7309
 * @private
7310
 * @details A single-dot path segment must be "." or an ASCII case-insensitive
7311
 * match for "%2e".
7312
 */
7313
ada_really_inline constexpr bool is_single_dot_path_segment(
7314
    std::string_view input) noexcept;
7315
7316
/**
7317
 * @private
7318
 * @details ipv4 character might contain 0-9 or a-f character ranges.
7319
 */
7320
ada_really_inline constexpr bool is_lowercase_hex(char c) noexcept;
7321
7322
/**
7323
 * @private
7324
 * @details Convert hex to binary. Caller is responsible to ensure that
7325
 * the parameter is an hexadecimal digit (0-9, A-F, a-f).
7326
 */
7327
ada_really_inline unsigned constexpr convert_hex_to_binary(char c) noexcept;
7328
7329
/**
7330
 * @private
7331
 * first_percent should be  = input.find('%')
7332
 *
7333
 * @todo It would be faster as noexcept maybe, but it could be unsafe since.
7334
 * @author Node.js
7335
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245
7336
 * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom
7337
 */
7338
std::string percent_decode(std::string_view input, size_t first_percent);
7339
7340
/**
7341
 * Decode an application/x-www-form-urlencoded component: map '+' to space,
7342
 * then percent-decode. Single allocation; no intermediate string.
7343
 *
7344
 * @param input A form-urlencoded component (key or value).
7345
 * @return The decoded string.
7346
 * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
7347
 */
7348
std::string form_urlencoded_decode(std::string_view input);
7349
7350
/**
7351
 * @private
7352
 * Returns a percent-encoding string whether percent encoding was needed or not.
7353
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7354
 */
7355
std::string percent_encode(std::string_view input,
7356
                           const uint8_t character_set[]);
7357
/**
7358
 * @private
7359
 * Returns a percent-encoded string version of input, while starting the percent
7360
 * encoding at the provided index.
7361
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7362
 */
7363
std::string percent_encode(std::string_view input,
7364
                           const uint8_t character_set[], size_t index);
7365
/**
7366
 * @private
7367
 * Returns true if percent encoding was needed, in which case, we store
7368
 * the percent-encoded content in 'out'. If the boolean 'append' is set to
7369
 * true, the content is appended to 'out'.
7370
 * If percent encoding is not needed, out is left unchanged.
7371
 * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226
7372
 */
7373
template <bool append>
7374
bool percent_encode(std::string_view input, const uint8_t character_set[],
7375
                    std::string& out);
7376
/**
7377
 * @private
7378
 * Returns the index at which percent encoding should start, or (equivalently),
7379
 * the length of the prefix that does not require percent encoding.
7380
 */
7381
ada_really_inline size_t percent_encode_index(std::string_view input,
7382
                                              const uint8_t character_set[]);
7383
/**
7384
 * @private
7385
 * Lowers the string in-place, assuming that the content is ASCII.
7386
 * Return true if the content was ASCII.
7387
 */
7388
constexpr bool to_lower_ascii(char* input, size_t length) noexcept;
7389
}  // namespace ada::unicode
7390
7391
#endif  // ADA_UNICODE_H
7392
/* end file include/ada/unicode.h */
7393
/* begin file include/ada/url_base-inl.h */
7394
/**
7395
 * @file url_base-inl.h
7396
 * @brief Inline functions for url base
7397
 */
7398
#ifndef ADA_URL_BASE_INL_H
7399
#define ADA_URL_BASE_INL_H
7400
7401
7402
#include <cstdint>
7403
#include <string>
7404
#if ADA_REGULAR_VISUAL_STUDIO
7405
#include <intrin.h>
7406
#endif  // ADA_REGULAR_VISUAL_STUDIO
7407
7408
namespace ada {
7409
7410
[[nodiscard]] ada_really_inline constexpr bool url_base::is_special()
7411
3.52M
    const noexcept {
7412
3.52M
  return type != ada::scheme::NOT_SPECIAL;
7413
3.52M
}
7414
7415
0
[[nodiscard]] inline uint16_t url_base::get_special_port() const noexcept {
7416
0
  return ada::scheme::get_special_port(type);
7417
0
}
7418
7419
[[nodiscard]] ada_really_inline uint16_t
7420
27.0k
url_base::scheme_default_port() const noexcept {
7421
27.0k
  return scheme::get_special_port(type);
7422
27.0k
}
7423
7424
}  // namespace ada
7425
7426
#endif  // ADA_URL_BASE_INL_H
7427
/* end file include/ada/url_base-inl.h */
7428
/* begin file include/ada/url-inl.h */
7429
/**
7430
 * @file url-inl.h
7431
 * @brief Definitions for the URL
7432
 */
7433
#ifndef ADA_URL_INL_H
7434
#define ADA_URL_INL_H
7435
7436
7437
#include <charconv>
7438
#include <cstdint>
7439
#include <cstring>
7440
#include <optional>
7441
#include <ostream>
7442
#include <string>
7443
#include <string_view>
7444
#include <system_error>
7445
#include <utility>
7446
#if ADA_REGULAR_VISUAL_STUDIO
7447
#include <intrin.h>
7448
#endif  // ADA_REGULAR_VISUAL_STUDIO
7449
7450
namespace ada {
7451
0
[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
7452
0
  return !username.empty() || !password.empty();
7453
0
}
7454
0
[[nodiscard]] ada_really_inline bool url::has_port() const noexcept {
7455
0
  return port.has_value();
7456
0
}
7457
0
[[nodiscard]] inline bool url::cannot_have_credentials_or_port() const {
7458
0
  return !host.has_value() || host->empty() || type == ada::scheme::type::FILE;
7459
0
}
7460
0
[[nodiscard]] inline bool url::has_empty_hostname() const noexcept {
7461
0
  if (!host.has_value()) {
7462
0
    return false;
7463
0
  }
7464
0
  return host->empty();
7465
0
}
7466
0
[[nodiscard]] inline bool url::has_hostname() const noexcept {
7467
0
  return host.has_value();
7468
0
}
7469
0
inline std::ostream& operator<<(std::ostream& out, const ada::url& u) {
7470
0
  return out << u.to_string();
7471
0
}
7472
7473
0
[[nodiscard]] size_t url::get_pathname_length() const noexcept {
7474
0
  return path.size();
7475
0
}
7476
7477
0
[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
7478
0
  return path;
7479
0
}
7480
7481
[[nodiscard]] ada_really_inline ada::url_components url::get_components()
7482
0
    const {
7483
0
  url_components out{};
7484
0
7485
0
  // protocol ends with ':'. for example: "https:"
7486
0
  out.protocol_end = uint32_t(get_protocol().size());
7487
0
7488
0
  // Trailing index is always the next character of the current one.
7489
0
  // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
7490
0
  size_t running_index = out.protocol_end;
7491
0
7492
0
  if (host.has_value()) {
7493
0
    // 2 characters for "//" and 1 character for starting index
7494
0
    out.host_start = out.protocol_end + 2;
7495
0
7496
0
    if (has_credentials()) {
7497
0
      out.username_end = uint32_t(out.host_start + username.size());
7498
0
7499
0
      out.host_start += uint32_t(username.size());
7500
0
7501
0
      if (!password.empty()) {
7502
0
        out.host_start += uint32_t(password.size() + 1);
7503
0
      }
7504
0
7505
0
      // host_start is the '@' when there are credentials, exactly as
7506
0
      // url_aggregator reports it, so the host itself begins one byte later.
7507
0
      out.host_end = uint32_t(out.host_start + 1 + host->size());
7508
0
    } else {
7509
0
      out.username_end = out.host_start;
7510
0
7511
0
      // Host does not start with "@" if it does not include credentials.
7512
0
      out.host_end = uint32_t(out.host_start + host->size());
7513
0
    }
7514
0
7515
0
    // host_end is one past the host, so it is already the next index.
7516
0
    running_index = out.host_end;
7517
0
  } else {
7518
0
    // Update host start and end date to the same index, since it does not
7519
0
    // exist.
7520
0
    out.host_start = out.protocol_end;
7521
0
    out.host_end = out.host_start;
7522
0
7523
0
    if (!has_opaque_path && path.starts_with("//")) {
7524
0
      // If url's host is null, url does not have an opaque path, url's path's
7525
0
      // size is greater than 1, and url's path[0] is the empty string, then
7526
0
      // append U+002F (/) followed by U+002E (.) to output.
7527
0
      running_index = out.protocol_end + 2;
7528
0
    } else {
7529
0
      running_index = out.protocol_end;
7530
0
    }
7531
0
  }
7532
0
7533
0
  if (port.has_value()) {
7534
0
    out.port = *port;
7535
0
    running_index += helpers::fast_digit_count(*port) + 1;  // Port omits ':'
7536
0
  }
7537
0
7538
0
  out.pathname_start = uint32_t(running_index);
7539
0
7540
0
  running_index += path.size();
7541
0
7542
0
  if (query.has_value()) {
7543
0
    out.search_start = uint32_t(running_index);
7544
0
    running_index += get_search().size();
7545
0
    if (get_search().empty()) {
7546
0
      running_index++;
7547
0
    }
7548
0
  }
7549
0
7550
0
  if (hash.has_value()) {
7551
0
    out.hash_start = uint32_t(running_index);
7552
0
  }
7553
0
7554
0
  return out;
7555
0
}
7556
7557
0
inline void url::update_base_hostname(std::string_view input) { host = input; }
7558
7559
0
inline void url::update_unencoded_base_hash(std::string_view input) {
7560
  // We do the percent encoding
7561
0
  hash = unicode::percent_encode(input,
7562
0
                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
7563
0
}
7564
7565
inline void url::update_base_search(std::string_view input,
7566
0
                                    const uint8_t query_percent_encode_set[]) {
7567
0
  query = ada::unicode::percent_encode(input, query_percent_encode_set);
7568
0
}
7569
7570
0
inline void url::update_base_search(std::optional<std::string>&& input) {
7571
0
  query = std::move(input);
7572
0
}
7573
7574
0
inline void url::update_base_pathname(const std::string_view input) {
7575
0
  path = input;
7576
0
}
7577
7578
0
inline void url::update_base_username(const std::string_view input) {
7579
0
  username = input;
7580
0
}
7581
7582
0
inline void url::update_base_password(const std::string_view input) {
7583
0
  password = input;
7584
0
}
7585
7586
0
inline void url::update_base_port(std::optional<uint16_t> input) {
7587
0
  port = input;
7588
0
}
7589
7590
0
constexpr void url::clear_pathname() { path.clear(); }
7591
7592
0
constexpr void url::clear_search() { query = std::nullopt; }
7593
7594
0
[[nodiscard]] constexpr bool url::has_hash() const noexcept {
7595
0
  return hash.has_value();
7596
0
}
7597
7598
0
[[nodiscard]] constexpr bool url::has_search() const noexcept {
7599
0
  return query.has_value();
7600
0
}
7601
7602
0
constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
7603
7604
0
inline void url::set_scheme(std::string&& new_scheme) noexcept {
7605
0
  type = ada::scheme::get_scheme_type(new_scheme);
7606
  // We only move the 'scheme' if it is non-special.
7607
0
  if (!is_special()) {
7608
0
    non_special_scheme = std::move(new_scheme);
7609
0
  }
7610
0
}
7611
7612
0
constexpr void url::copy_scheme(ada::url&& u) {
7613
0
  non_special_scheme = u.non_special_scheme;
7614
0
  type = u.type;
7615
0
}
7616
7617
0
constexpr void url::copy_scheme(const ada::url& u) {
7618
0
  non_special_scheme = u.non_special_scheme;
7619
0
  type = u.type;
7620
0
}
7621
7622
0
[[nodiscard]] ada_really_inline std::string url::get_href() const {
7623
0
  if (is_special() && host.has_value() && username.empty() &&
7624
0
      password.empty() && !port.has_value()) [[likely]] {
7625
0
    const std::string_view scheme = ada::scheme::details::is_special_list[type];
7626
0
    const size_t host_size = host->size();
7627
0
    const size_t path_size = path.size();
7628
0
    const size_t query_size = query.has_value() ? query->size() : 0;
7629
0
    const size_t hash_size = hash.has_value() ? hash->size() : 0;
7630
0
    const size_t total = scheme.size() + 3 + host_size + path_size +
7631
0
                         (query.has_value() ? query_size + 1 : 0) +
7632
0
                         (hash.has_value() ? hash_size + 1 : 0);
7633
0
    std::string output(total, '\0');
7634
0
    char* p = output.data();
7635
0
    std::memcpy(p, scheme.data(), scheme.size());
7636
0
    p += scheme.size();
7637
0
    p[0] = ':';
7638
0
    p[1] = '/';
7639
0
    p[2] = '/';
7640
0
    p += 3;
7641
0
    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7642
0
    std::memcpy(p, host->data(), host_size);
7643
0
    p += host_size;
7644
0
    std::memcpy(p, path.data(), path_size);
7645
0
    p += path_size;
7646
0
    if (query.has_value()) {
7647
0
      *p++ = '?';
7648
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7649
0
      std::memcpy(p, query->data(), query_size);
7650
0
      p += query_size;
7651
0
    }
7652
0
    if (hash.has_value()) {
7653
0
      *p++ = '#';
7654
0
      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
7655
0
      std::memcpy(p, hash->data(), hash_size);
7656
0
    }
7657
0
    return output;
7658
0
  }
7659
0
7660
0
  std::string output;
7661
0
  output.reserve(get_href_size());
7662
0
7663
0
  if (is_special()) {
7664
0
    output.append(ada::scheme::details::is_special_list[type]);
7665
0
    output += ':';
7666
0
  } else {
7667
0
    output.append(non_special_scheme);
7668
0
    output += ':';
7669
0
  }
7670
0
7671
0
  if (host.has_value()) {
7672
0
    output += '/';
7673
0
    output += '/';
7674
0
    if (has_credentials()) {
7675
0
      output.append(username);
7676
0
      if (!password.empty()) {
7677
0
        output += ':';
7678
0
        output.append(password);
7679
0
      }
7680
0
      output += '@';
7681
0
    }
7682
0
    output.append(*host);
7683
0
    if (port.has_value()) {
7684
0
      output += ':';
7685
0
      char port_buf[5];
7686
0
      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
7687
0
      (void)ec;
7688
0
      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
7689
0
    }
7690
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7691
0
    // If url's host is null, url does not have an opaque path, url's path's
7692
0
    // size is greater than 1, and url's path[0] is the empty string, then
7693
0
    // append U+002F (/) followed by U+002E (.) to output.
7694
0
    output += '/';
7695
0
    output += '.';
7696
0
  }
7697
0
  output.append(path);
7698
0
  if (query.has_value()) {
7699
0
    output += '?';
7700
0
    output.append(*query);
7701
0
  }
7702
0
  if (hash.has_value()) {
7703
0
    output += '#';
7704
0
    output.append(*hash);
7705
0
  }
7706
0
  return output;
7707
0
}
7708
7709
0
[[nodiscard]] inline size_t url::get_href_size() const noexcept {
7710
0
  size_t size = 0;
7711
0
  if (is_special()) {
7712
0
    size += ada::scheme::details::is_special_list[type].size() + 1;
7713
0
  } else {
7714
0
    size += non_special_scheme.size() + 1;
7715
0
  }
7716
0
  if (host.has_value()) {
7717
0
    size += host->size();
7718
0
    size += 2;
7719
0
    if (has_credentials()) {
7720
0
      size += username.size();
7721
0
      if (!password.empty()) {
7722
0
        size += 1 + password.size();
7723
0
      }
7724
0
      size += 1;
7725
0
    }
7726
0
    if (port.has_value()) {
7727
0
      size += 1;
7728
0
      uint16_t p = *port;
7729
0
      size += (p >= 10000)  ? 5
7730
0
              : (p >= 1000) ? 4
7731
0
              : (p >= 100)  ? 3
7732
0
              : (p >= 10)   ? 2
7733
0
                            : 1;
7734
0
    }
7735
0
  } else if (!has_opaque_path && path.starts_with("//")) {
7736
0
    size += 2;
7737
0
  }
7738
0
  size += path.size();
7739
0
  if (query.has_value()) {
7740
0
    size += 1 + query->size();
7741
0
  }
7742
0
  if (hash.has_value()) {
7743
0
    size += 1 + hash->size();
7744
0
  }
7745
0
  return size;
7746
0
}
7747
7748
ada_really_inline size_t url::parse_port(std::string_view view,
7749
0
                                         bool check_trailing_content) noexcept {
7750
0
  ada_log("parse_port('", view, "') ", view.size());
7751
0
  if (!view.empty() && view[0] == '-') {
7752
0
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
7753
0
    is_valid = false;
7754
0
    return 0;
7755
0
  }
7756
0
  uint16_t parsed_port{};
7757
0
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
7758
0
  if (r.ec == std::errc::result_out_of_range) {
7759
0
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
7760
0
    is_valid = false;
7761
0
    return 0;
7762
0
  }
7763
0
  ada_log("parse_port: ", parsed_port);
7764
0
  const auto consumed = size_t(r.ptr - view.data());
7765
0
  ada_log("parse_port: consumed ", consumed);
7766
0
  if (check_trailing_content) {
7767
0
    is_valid &=
7768
0
        (consumed == view.size() || view[consumed] == '/' ||
7769
0
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
7770
0
  }
7771
0
  ada_log("parse_port: is_valid = ", is_valid);
7772
0
  if (is_valid) {
7773
    // scheme_default_port can return 0, and we should allow 0 as a base port.
7774
0
    auto default_port = scheme_default_port();
7775
0
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
7776
0
                         (default_port != parsed_port);
7777
0
    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
7778
0
                                                  : std::nullopt;
7779
0
  }
7780
0
  return consumed;
7781
0
}
7782
7783
}  // namespace ada
7784
7785
#endif  // ADA_URL_H
7786
/* end file include/ada/url-inl.h */
7787
/* begin file include/ada/url_components-inl.h */
7788
/**
7789
 * @file url_components.h
7790
 * @brief Declaration for the URL Components
7791
 */
7792
#ifndef ADA_URL_COMPONENTS_INL_H
7793
#define ADA_URL_COMPONENTS_INL_H
7794
7795
7796
#include <cstdint>
7797
7798
namespace ada {
7799
7800
[[nodiscard]] constexpr bool url_components::check_offset_consistency()
7801
0
    const noexcept {
7802
0
  /**
7803
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
7804
0
   *       |     |    |          | ^^^^|       |   |
7805
0
   *       |     |    |          | |   |       |   `----- hash_start
7806
0
   *       |     |    |          | |   |       `--------- search_start
7807
0
   *       |     |    |          | |   `----------------- pathname_start
7808
0
   *       |     |    |          | `--------------------- port
7809
0
   *       |     |    |          `----------------------- host_end
7810
0
   *       |     |    `---------------------------------- host_start
7811
0
   *       |     `--------------------------------------- username_end
7812
0
   *       `--------------------------------------------- protocol_end
7813
0
   */
7814
0
  // These conditions can be made more strict.
7815
0
  if (protocol_end == url_components::omitted) {
7816
0
    return false;
7817
0
  }
7818
0
  uint32_t index = protocol_end;
7819
0
7820
0
  if (username_end == url_components::omitted) {
7821
0
    return false;
7822
0
  }
7823
0
  if (username_end < index) {
7824
0
    return false;
7825
0
  }
7826
0
  index = username_end;
7827
0
7828
0
  if (host_start == url_components::omitted) {
7829
0
    return false;
7830
0
  }
7831
0
  if (host_start < index) {
7832
0
    return false;
7833
0
  }
7834
0
  index = host_start;
7835
0
7836
0
  if (port != url_components::omitted) {
7837
0
    if (port > 0xffff) {
7838
0
      return false;
7839
0
    }
7840
0
    uint32_t port_length = helpers::fast_digit_count(port) + 1;
7841
0
    if (index + port_length < index) {
7842
0
      return false;
7843
0
    }
7844
0
    index += port_length;
7845
0
  }
7846
0
7847
0
  if (pathname_start == url_components::omitted) {
7848
0
    return false;
7849
0
  }
7850
0
  if (pathname_start < index) {
7851
0
    return false;
7852
0
  }
7853
0
  index = pathname_start;
7854
0
7855
0
  if (search_start != url_components::omitted) {
7856
0
    if (search_start < index) {
7857
0
      return false;
7858
0
    }
7859
0
    index = search_start;
7860
0
  }
7861
0
7862
0
  if (hash_start != url_components::omitted) {
7863
0
    if (hash_start < index) {
7864
0
      return false;
7865
0
    }
7866
0
  }
7867
0
7868
0
  return true;
7869
0
}
7870
7871
}  // namespace ada
7872
#endif
7873
/* end file include/ada/url_components-inl.h */
7874
/* begin file include/ada/url_aggregator.h */
7875
/**
7876
 * @file url_aggregator.h
7877
 * @brief Declaration for the `ada::url_aggregator` class.
7878
 *
7879
 * This file contains the `ada::url_aggregator` struct which represents a parsed
7880
 * URL using a single buffer with component offsets. This is the default and
7881
 * most memory-efficient URL representation in Ada.
7882
 *
7883
 * @see url.h for an alternative representation using separate strings
7884
 */
7885
#ifndef ADA_URL_AGGREGATOR_H
7886
#define ADA_URL_AGGREGATOR_H
7887
7888
#include <cstddef>
7889
#include <cstdint>
7890
#include <ostream>
7891
#include <string>
7892
#include <string_view>
7893
#include <variant>
7894
7895
7896
namespace ada {
7897
7898
namespace parser {}
7899
7900
/**
7901
 * @brief Memory-efficient URL representation using a single buffer.
7902
 *
7903
 * The `url_aggregator` stores the entire normalized URL in a single string
7904
 * buffer and tracks component boundaries using offsets. This design minimizes
7905
 * memory allocations and is ideal for read-mostly access patterns.
7906
 *
7907
 * Getter methods return `std::string_view` pointing into the internal buffer.
7908
 * These views are lightweight (no allocation) but become invalid if the
7909
 * url_aggregator is modified or destroyed.
7910
 *
7911
 * @warning Views returned by getters (e.g., `get_pathname()`) are invalidated
7912
 * when any setter is called. Do not use a getter's result as input to a
7913
 * setter on the same object without copying first.
7914
 *
7915
 * @note This is the default URL type returned by `ada::parse()`.
7916
 *
7917
 * @see url For an alternative using separate std::string instances
7918
 */
7919
struct url_aggregator : url_base {
7920
1.44M
  url_aggregator() = default;
7921
0
  url_aggregator(const url_aggregator& u) = default;
7922
1.16M
  url_aggregator(url_aggregator&& u) noexcept = default;
7923
0
  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
7924
0
  url_aggregator& operator=(const url_aggregator& u) = default;
7925
2.61M
  ~url_aggregator() override = default;
7926
7927
  /**
7928
   * The setter functions follow the steps defined in the URL Standard.
7929
   *
7930
   * The url_aggregator has a single buffer that contains the entire normalized
7931
   * URL. The various components are represented as offsets into that buffer.
7932
   * When you call get_pathname(), for example, you get a std::string_view that
7933
   * points into that buffer. If the url_aggregator is modified, the buffer may
7934
   * be reallocated, and the std::string_view you obtained earlier may become
7935
   * invalid. In particular, this implies that you cannot modify the URL using
7936
   * a setter function with a std::string_view that points into the
7937
   * url_aggregator E.g., the following is incorrect:
7938
   * url->set_hostname(url->get_pathname()).
7939
   * You must first copy the pathname to a separate string.
7940
   * std::string pathname(url->get_pathname());
7941
   * url->set_hostname(pathname);
7942
   *
7943
   * The caller is responsible for ensuring that the url_aggregator is not
7944
   * modified while any std::string_view obtained from it is in use.
7945
   */
7946
  bool set_href(std::string_view input);
7947
  bool set_host(std::string_view input);
7948
  bool set_hostname(std::string_view input);
7949
  bool set_protocol(std::string_view input);
7950
  bool set_username(std::string_view input);
7951
  bool set_password(std::string_view input);
7952
  bool set_port(std::string_view input);
7953
  bool set_pathname(std::string_view input);
7954
  void set_search(std::string_view input);
7955
  void set_hash(std::string_view input);
7956
7957
  /**
7958
   * Validates whether the hostname is a valid domain according to RFC 1034.
7959
   * @return `true` if the domain is valid, `false` otherwise.
7960
   */
7961
  [[nodiscard]] bool has_valid_domain() const noexcept override;
7962
7963
  /**
7964
   * Returns the URL's origin (scheme + host + port for special URLs).
7965
   * @return A newly allocated string containing the serialized origin.
7966
   * @see https://url.spec.whatwg.org/#concept-url-origin
7967
   */
7968
  [[nodiscard]] std::string get_origin() const override;
7969
7970
  /**
7971
   * Returns the full serialized URL (the href) as a string_view.
7972
   * Does not allocate memory. The returned view becomes invalid if this
7973
   * url_aggregator is modified or destroyed.
7974
   * @return A string_view into the internal buffer.
7975
   * @see https://url.spec.whatwg.org/#dom-url-href
7976
   */
7977
  [[nodiscard]] constexpr std::string_view get_href() const noexcept
7978
      ada_lifetime_bound;
7979
7980
  /**
7981
   * Returns the byte length of the serialized URL without allocating a string.
7982
   * @return Size of the href in bytes.
7983
   */
7984
  [[nodiscard]] constexpr size_t get_href_size() const noexcept;
7985
7986
  /**
7987
   * Returns the URL's username component.
7988
   * Does not allocate memory. The returned view becomes invalid if this
7989
   * url_aggregator is modified or destroyed.
7990
   * @return A string_view of the username.
7991
   * @see https://url.spec.whatwg.org/#dom-url-username
7992
   */
7993
  [[nodiscard]] std::string_view get_username() const ada_lifetime_bound;
7994
7995
  /**
7996
   * Returns the URL's password component.
7997
   * Does not allocate memory. The returned view becomes invalid if this
7998
   * url_aggregator is modified or destroyed.
7999
   * @return A string_view of the password.
8000
   * @see https://url.spec.whatwg.org/#dom-url-password
8001
   */
8002
  [[nodiscard]] std::string_view get_password() const ada_lifetime_bound;
8003
8004
  /**
8005
   * Returns the URL's port as a string (e.g., "8080").
8006
   * Does not allocate memory. Returns empty view if no port is set.
8007
   * The returned view becomes invalid if this url_aggregator is modified.
8008
   * @return A string_view of the port.
8009
   * @see https://url.spec.whatwg.org/#dom-url-port
8010
   */
8011
  [[nodiscard]] std::string_view get_port() const ada_lifetime_bound;
8012
8013
  /**
8014
   * Returns the URL's fragment prefixed with '#' (e.g., "#section").
8015
   * Does not allocate memory. Returns empty view if no fragment is set.
8016
   * The returned view becomes invalid if this url_aggregator is modified.
8017
   * @return A string_view of the hash.
8018
   * @see https://url.spec.whatwg.org/#dom-url-hash
8019
   */
8020
  [[nodiscard]] std::string_view get_hash() const ada_lifetime_bound;
8021
8022
  /**
8023
   * Returns the URL's host and port (e.g., "example.com:8080").
8024
   * Does not allocate memory. Returns empty view if no host is set.
8025
   * The returned view becomes invalid if this url_aggregator is modified.
8026
   * @return A string_view of host:port.
8027
   * @see https://url.spec.whatwg.org/#dom-url-host
8028
   */
8029
  [[nodiscard]] std::string_view get_host() const ada_lifetime_bound;
8030
8031
  /**
8032
   * Returns the URL's hostname (without port).
8033
   * Does not allocate memory. Returns empty view if no host is set.
8034
   * The returned view becomes invalid if this url_aggregator is modified.
8035
   * @return A string_view of the hostname.
8036
   * @see https://url.spec.whatwg.org/#dom-url-hostname
8037
   */
8038
  [[nodiscard]] std::string_view get_hostname() const ada_lifetime_bound;
8039
8040
  /**
8041
   * Returns the URL's path component.
8042
   * Does not allocate memory. The returned view becomes invalid if this
8043
   * url_aggregator is modified or destroyed.
8044
   * @return A string_view of the pathname.
8045
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8046
   */
8047
  [[nodiscard]] constexpr std::string_view get_pathname() const
8048
      ada_lifetime_bound;
8049
8050
  /**
8051
   * Returns the byte length of the pathname without creating a string.
8052
   * @return Size of the pathname in bytes.
8053
   * @see https://url.spec.whatwg.org/#dom-url-pathname
8054
   */
8055
  [[nodiscard]] ada_really_inline uint32_t get_pathname_length() const noexcept;
8056
8057
  /**
8058
   * Returns the URL's query string prefixed with '?' (e.g., "?foo=bar").
8059
   * Does not allocate memory. Returns empty view if no query is set.
8060
   * The returned view becomes invalid if this url_aggregator is modified.
8061
   * @return A string_view of the search/query.
8062
   * @see https://url.spec.whatwg.org/#dom-url-search
8063
   */
8064
  [[nodiscard]] std::string_view get_search() const ada_lifetime_bound;
8065
8066
  /**
8067
   * Returns the URL's scheme followed by a colon (e.g., "https:").
8068
   * Does not allocate memory. The returned view becomes invalid if this
8069
   * url_aggregator is modified or destroyed.
8070
   * @return A string_view of the protocol.
8071
   * @see https://url.spec.whatwg.org/#dom-url-protocol
8072
   */
8073
  [[nodiscard]] std::string_view get_protocol() const ada_lifetime_bound;
8074
8075
  /**
8076
   * Checks if the URL has credentials (non-empty username or password).
8077
   * @return `true` if username or password is non-empty, `false` otherwise.
8078
   */
8079
  [[nodiscard]] ada_really_inline constexpr bool has_credentials()
8080
      const noexcept;
8081
8082
  /**
8083
   * Returns the URL component offsets for efficient serialization.
8084
   *
8085
   * The components represent byte offsets into the serialized URL:
8086
   * ```
8087
   * https://user:pass@example.com:1234/foo/bar?baz#quux
8088
   *       |     |    |          | ^^^^|       |   |
8089
   *       |     |    |          | |   |       |   `----- hash_start
8090
   *       |     |    |          | |   |       `--------- search_start
8091
   *       |     |    |          | |   `----------------- pathname_start
8092
   *       |     |    |          | `--------------------- port
8093
   *       |     |    |          `----------------------- host_end
8094
   *       |     |    `---------------------------------- host_start
8095
   *       |     `--------------------------------------- username_end
8096
   *       `--------------------------------------------- protocol_end
8097
   * ```
8098
   * @return A constant reference to the url_components struct.
8099
   * @see https://github.com/servo/rust-url
8100
   */
8101
  [[nodiscard]] ada_really_inline const url_components& get_components()
8102
      const noexcept;
8103
8104
  /**
8105
   * Returns a JSON string representation of this URL for debugging.
8106
   * @return A JSON-formatted string with all URL components.
8107
   */
8108
  [[nodiscard]] std::string to_string() const override;
8109
8110
  /**
8111
   * Returns a visual diagram showing component boundaries in the URL.
8112
   * Useful for debugging and understanding URL structure.
8113
   * @return A multi-line string diagram.
8114
   */
8115
  [[nodiscard]] std::string to_diagram() const;
8116
8117
  /**
8118
   * Validates internal consistency of component offsets (for debugging).
8119
   * @return `true` if offsets are consistent, `false` if corrupted.
8120
   */
8121
  [[nodiscard]] constexpr bool validate() const noexcept;
8122
8123
  /**
8124
   * Checks if the URL has an empty hostname (host is set but empty string).
8125
   * @return `true` if host exists but is empty, `false` otherwise.
8126
   */
8127
  [[nodiscard]] constexpr bool has_empty_hostname() const noexcept;
8128
8129
  /**
8130
   * Checks if the URL has a hostname (including empty hostnames).
8131
   * @return `true` if host is present, `false` otherwise.
8132
   */
8133
  [[nodiscard]] constexpr bool has_hostname() const noexcept;
8134
8135
  /**
8136
   * Checks if the URL has a non-empty username.
8137
   * @return `true` if username is non-empty, `false` otherwise.
8138
   */
8139
  [[nodiscard]] constexpr bool has_non_empty_username() const noexcept;
8140
8141
  /**
8142
   * Checks if the URL has a non-empty password.
8143
   * @return `true` if password is non-empty, `false` otherwise.
8144
   */
8145
  [[nodiscard]] constexpr bool has_non_empty_password() const noexcept;
8146
8147
  /**
8148
   * Checks if the URL has a non-default port explicitly specified.
8149
   * @return `true` if a port is present, `false` otherwise.
8150
   */
8151
  [[nodiscard]] constexpr bool has_port() const noexcept;
8152
8153
  /**
8154
   * Checks if the URL has a password component (may be empty).
8155
   * @return `true` if password is present, `false` otherwise.
8156
   */
8157
  [[nodiscard]] constexpr bool has_password() const noexcept;
8158
8159
  /**
8160
   * Checks if the URL has a fragment/hash component.
8161
   * @return `true` if hash is present, `false` otherwise.
8162
   */
8163
  [[nodiscard]] constexpr bool has_hash() const noexcept override;
8164
8165
  /**
8166
   * Checks if the URL has a query/search component.
8167
   * @return `true` if query is present, `false` otherwise.
8168
   */
8169
  [[nodiscard]] constexpr bool has_search() const noexcept override;
8170
8171
  /**
8172
   * Removes the port from the URL.
8173
   */
8174
  inline void clear_port();
8175
8176
  /**
8177
   * Removes the hash/fragment from the URL.
8178
   */
8179
  inline void clear_hash();
8180
8181
  /**
8182
   * Removes the query/search string from the URL.
8183
   */
8184
  inline void clear_search() override;
8185
8186
 private:
8187
  // helper methods
8188
  friend void helpers::strip_trailing_spaces_from_opaque_path<url_aggregator>(
8189
      url_aggregator& url);
8190
  // parse_url methods
8191
  friend url_aggregator parser::parse_url<url_aggregator>(
8192
      std::string_view, const url_aggregator*);
8193
8194
  friend url_aggregator parser::parse_url_impl<url_aggregator, true>(
8195
      std::string_view, const url_aggregator*);
8196
  friend url_aggregator parser::parse_url_impl<url_aggregator, false>(
8197
      std::string_view, const url_aggregator*);
8198
  template <class result_type>
8199
  friend bool parser::try_parse_simple_absolute(std::string_view, result_type&);
8200
  template <class result_type>
8201
  friend bool parser::finish_simple_absolute_with_port(std::string_view,
8202
                                                       result_type&,
8203
                                                       ada::scheme::type,
8204
                                                       uint32_t, size_t, size_t,
8205
                                                       size_t, bool);
8206
  template <class result_type>
8207
  friend bool parser::try_parse_simple_relative(std::string_view,
8208
                                                const result_type&,
8209
                                                result_type&);
8210
8211
#if ADA_INCLUDE_URL_PATTERN
8212
  // url_pattern methods
8213
  template <url_pattern_regex::regex_concept regex_provider>
8214
  friend tl::expected<url_pattern<regex_provider>, errors>
8215
  parse_url_pattern_impl(
8216
      std::variant<std::string_view, url_pattern_init>&& input,
8217
      const std::string_view* base_url, const url_pattern_options* options);
8218
#endif  // ADA_INCLUDE_URL_PATTERN
8219
8220
  // components is declared before buffer so that the offset fields land
8221
  // close to url_base in memory, improving cache locality for getter calls.
8222
  // Note: exact cache-line placement is implementation- and platform-dependent.
8223
  url_components components{};
8224
  std::string buffer{};
8225
8226
  /**
8227
   * Returns true if neither the search, nor the hash nor the pathname
8228
   * have been set.
8229
   * @return true if the buffer is ready to receive the path.
8230
   */
8231
  [[nodiscard]] ada_really_inline bool is_at_path() const noexcept;
8232
8233
  inline void add_authority_slashes_if_needed();
8234
8235
  /**
8236
   * To optimize performance, you may indicate how much memory to allocate
8237
   * within this instance.
8238
   */
8239
  constexpr void reserve(uint32_t capacity);
8240
8241
  ada_really_inline size_t parse_port(std::string_view view,
8242
                                      bool check_trailing_content) override;
8243
8244
0
  ada_really_inline size_t parse_port(std::string_view view) override {
8245
0
    return this->parse_port(view, false);
8246
0
  }
8247
8248
  /**
8249
   * Return true on success. The 'in_place' parameter indicates whether the
8250
   * the string_view input is pointing in the buffer. When in_place is false,
8251
   * we must nearly always update the buffer.
8252
   * @see https://url.spec.whatwg.org/#concept-ipv4-parser
8253
   */
8254
  [[nodiscard]] bool parse_ipv4(std::string_view input, bool in_place);
8255
8256
  /**
8257
   * Return true on success.
8258
   * @see https://url.spec.whatwg.org/#concept-ipv6-parser
8259
   */
8260
  [[nodiscard]] bool parse_ipv6(std::string_view input);
8261
8262
  /**
8263
   * Return true on success.
8264
   * @see https://url.spec.whatwg.org/#concept-opaque-host-parser
8265
   */
8266
  [[nodiscard]] bool parse_opaque_host(std::string_view input);
8267
8268
  ada_really_inline void parse_path(std::string_view input);
8269
8270
  /**
8271
   * A URL cannot have a username/password/port if its host is null or the empty
8272
   * string, or its scheme is "file".
8273
   */
8274
  [[nodiscard]] constexpr bool cannot_have_credentials_or_port() const;
8275
8276
  /**
8277
   * @private
8278
   * Several setters restore a saved copy of the URL when the mutation pushes
8279
   * the buffer past get_max_input_length(). A setter grows the buffer by at
8280
   * most input_len * 3 (worst-case percent-encoding) plus a small constant for
8281
   * delimiters, so this returns false when that upper bound stays within the
8282
   * limit. It does not account for parse-failure rollbacks, which are
8283
   * unrelated to the length limit.
8284
   */
8285
  [[nodiscard]] bool needs_rollback_snapshot(size_t input_len) const noexcept;
8286
8287
  template <bool override_hostname = false>
8288
  bool set_host_or_hostname(std::string_view input);
8289
8290
  ada_really_inline bool parse_host(std::string_view input);
8291
8292
  inline void update_base_authority(std::string_view base_buffer,
8293
                                    const url_components& base);
8294
  inline void update_unencoded_base_hash(std::string_view input);
8295
  inline void update_base_hostname(std::string_view input);
8296
  inline void update_base_search(std::string_view input);
8297
  inline void update_base_search(std::string_view input,
8298
                                 const uint8_t* query_percent_encode_set);
8299
  inline void update_base_pathname(std::string_view input);
8300
  inline void update_base_username(std::string_view input);
8301
  inline void append_base_username(std::string_view input);
8302
  inline void update_base_password(std::string_view input);
8303
  inline void append_base_password(std::string_view input);
8304
  inline void update_base_port(uint32_t input);
8305
  inline void append_base_pathname(std::string_view input);
8306
  [[nodiscard]] inline uint32_t retrieve_base_port() const;
8307
  constexpr void clear_hostname();
8308
  constexpr void clear_password();
8309
  constexpr void clear_pathname() override;
8310
  [[nodiscard]] constexpr bool has_dash_dot() const noexcept;
8311
  void delete_dash_dot();
8312
  inline void consume_prepared_path(std::string_view input);
8313
  template <bool has_state_override = false>
8314
  [[nodiscard]] ada_really_inline bool parse_scheme_with_colon(
8315
      std::string_view input);
8316
  ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end,
8317
                                                std::string_view input);
8318
  [[nodiscard]] constexpr bool has_authority() const noexcept;
8319
  constexpr void set_protocol_as_file();
8320
  inline void set_scheme(std::string_view new_scheme);
8321
  /**
8322
   * Fast function to set the scheme from a view with a colon in the
8323
   * buffer, does not change type.
8324
   */
8325
  inline void set_scheme_from_view_with_colon(
8326
      std::string_view new_scheme_with_colon);
8327
  inline void copy_scheme(const url_aggregator& u);
8328
8329
  inline void update_host_to_base_host(const std::string_view input);
8330
8331
};  // url_aggregator
8332
8333
inline std::ostream& operator<<(std::ostream& out, const url& u);
8334
}  // namespace ada
8335
8336
#endif
8337
/* end file include/ada/url_aggregator.h */
8338
/* begin file include/ada/url_aggregator-inl.h */
8339
/**
8340
 * @file url_aggregator-inl.h
8341
 * @brief Inline functions for url aggregator
8342
 */
8343
#ifndef ADA_URL_AGGREGATOR_INL_H
8344
#define ADA_URL_AGGREGATOR_INL_H
8345
8346
/* begin file include/ada/unicode-inl.h */
8347
/**
8348
 * @file unicode-inl.h
8349
 * @brief Definitions for unicode operations.
8350
 */
8351
#ifndef ADA_UNICODE_INL_H
8352
#define ADA_UNICODE_INL_H
8353
8354
#include <cstddef>
8355
#include <cstdint>
8356
#include <cstring>
8357
#include <string_view>
8358
8359
/**
8360
 * Unicode operations. These functions are not part of our public API and may
8361
 * change at any time.
8362
 *
8363
 * private
8364
 * @namespace ada::unicode
8365
 * @brief Includes the declarations for unicode operations
8366
 */
8367
namespace ada::unicode {
8368
ada_really_inline size_t percent_encode_index(const std::string_view input,
8369
3.18k
                                              const uint8_t character_set[]) {
8370
3.18k
  const char* data = input.data();
8371
3.18k
  const size_t size = input.size();
8372
8373
  // Process 8 bytes at a time using unrolled loop
8374
3.18k
  size_t i = 0;
8375
3.24k
  for (; i + 8 <= size; i += 8) {
8376
363
    unsigned char chunk[8];
8377
363
    std::memcpy(&chunk, data + i,
8378
363
                8);  // entices compiler to unconditionally process 8 characters
8379
8380
    // Check 8 characters at once
8381
1.15k
    for (size_t j = 0; j < 8; j++) {
8382
1.09k
      if (character_sets::bit_at(character_set, chunk[j])) {
8383
302
        return i + j;
8384
302
      }
8385
1.09k
    }
8386
363
  }
8387
8388
  // Handle remaining bytes
8389
5.23k
  for (; i < size; i++) {
8390
3.62k
    if (character_sets::bit_at(character_set, data[i])) {
8391
1.27k
      return i;
8392
1.27k
    }
8393
3.62k
  }
8394
8395
1.60k
  return size;
8396
2.88k
}
8397
}  // namespace ada::unicode
8398
8399
#endif  // ADA_UNICODE_INL_H
8400
/* end file include/ada/unicode-inl.h */
8401
8402
#include <charconv>
8403
#include <cstdint>
8404
#include <cstring>
8405
#include <ostream>
8406
#include <string>
8407
#include <string_view>
8408
#include <system_error>
8409
8410
namespace ada {
8411
8412
inline void url_aggregator::update_base_authority(
8413
0
    std::string_view base_buffer, const ada::url_components& base) {
8414
0
  std::string_view input = base_buffer.substr(
8415
0
      base.protocol_end, base.host_start - base.protocol_end);
8416
0
  ada_log("url_aggregator::update_base_authority ", input);
8417
8418
0
  bool input_starts_with_dash = input.starts_with("//");
8419
0
  uint32_t diff = components.host_start - components.protocol_end;
8420
8421
0
  buffer.erase(components.protocol_end,
8422
0
               components.host_start - components.protocol_end);
8423
0
  components.username_end = components.protocol_end;
8424
8425
0
  if (input_starts_with_dash) {
8426
0
    input.remove_prefix(2);
8427
0
    diff += 2;  // add "//"
8428
0
    buffer.insert(components.protocol_end, "//");
8429
0
    components.username_end += 2;
8430
0
  }
8431
8432
0
  size_t password_delimiter = input.find(':');
8433
8434
  // Check if input contains both username and password by checking the
8435
  // delimiter: ":" A typical input that contains authority would be "user:pass"
8436
0
  if (password_delimiter != std::string_view::npos) {
8437
    // Insert both username and password
8438
0
    std::string_view username = input.substr(0, password_delimiter);
8439
0
    std::string_view password = input.substr(password_delimiter + 1);
8440
8441
0
    buffer.insert(components.protocol_end + diff, username);
8442
0
    diff += uint32_t(username.size());
8443
0
    buffer.insert(components.protocol_end + diff, ":");
8444
0
    components.username_end = components.protocol_end + diff;
8445
0
    buffer.insert(components.protocol_end + diff + 1, password);
8446
0
    diff += uint32_t(password.size()) + 1;
8447
0
  } else if (!input.empty()) {
8448
    // Insert only username
8449
0
    buffer.insert(components.protocol_end + diff, input);
8450
0
    components.username_end =
8451
0
        components.protocol_end + diff + uint32_t(input.size());
8452
0
    diff += uint32_t(input.size());
8453
0
  }
8454
8455
0
  components.host_start += diff;
8456
8457
0
  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
8458
0
    buffer.insert(components.host_start, "@");
8459
0
    diff++;
8460
0
  }
8461
0
  components.host_end += diff;
8462
0
  components.pathname_start += diff;
8463
0
  if (components.search_start != url_components::omitted) {
8464
0
    components.search_start += diff;
8465
0
  }
8466
0
  if (components.hash_start != url_components::omitted) {
8467
0
    components.hash_start += diff;
8468
0
  }
8469
0
}
8470
8471
14.6k
inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
8472
14.6k
  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
8473
14.6k
          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
8474
14.6k
          " bytes] components.hash_start = ", components.hash_start);
8475
14.6k
  ADA_ASSERT_TRUE(validate());
8476
14.6k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8477
14.6k
  if (components.hash_start != url_components::omitted) {
8478
0
    buffer.resize(components.hash_start);
8479
0
  }
8480
14.6k
  components.hash_start = uint32_t(buffer.size());
8481
14.6k
  buffer += "#";
8482
14.6k
  bool encoding_required = unicode::percent_encode<true>(
8483
14.6k
      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
8484
  // When encoding_required is false, then buffer is left unchanged, and percent
8485
  // encoding was not deemed required.
8486
14.6k
  if (!encoding_required) {
8487
6.94k
    buffer.append(input);
8488
6.94k
  }
8489
14.6k
  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
8490
14.6k
          buffer, "' [", buffer.size(), " bytes]");
8491
14.6k
  ADA_ASSERT_TRUE(validate());
8492
14.6k
}
8493
8494
ada_really_inline uint32_t url_aggregator::replace_and_resize(
8495
313k
    uint32_t start, uint32_t end, std::string_view input) {
8496
313k
  uint32_t current_length = end - start;
8497
313k
  uint32_t input_size = uint32_t(input.size());
8498
313k
  uint32_t new_difference = input_size - current_length;
8499
8500
313k
  if (current_length == 0) {
8501
293k
    buffer.insert(start, input);
8502
293k
  } else if (input_size == current_length) {
8503
10.6k
    if (input_size != 0) {
8504
10.6k
      std::memmove(buffer.data() + start, input.data(), input_size);
8505
10.6k
    }
8506
10.6k
  } else if (input_size < current_length) {
8507
2.71k
    buffer.erase(start, current_length - input_size);
8508
2.71k
    buffer.replace(start, input_size, input);
8509
6.45k
  } else {
8510
6.45k
    buffer.replace(start, current_length, input.substr(0, current_length));
8511
6.45k
    buffer.insert(start + current_length, input.substr(current_length));
8512
6.45k
  }
8513
8514
313k
  return new_difference;
8515
313k
}
8516
8517
168k
inline void url_aggregator::update_base_hostname(const std::string_view input) {
8518
168k
  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
8519
168k
          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
8520
168k
  ADA_ASSERT_TRUE(validate());
8521
168k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8522
8523
  // This next line is required for when parsing a URL like `foo://`
8524
168k
  add_authority_slashes_if_needed();
8525
8526
168k
  bool has_credentials = components.protocol_end + 2 < components.host_start;
8527
168k
  uint32_t new_difference =
8528
168k
      replace_and_resize(components.host_start, components.host_end, input);
8529
8530
168k
  if (has_credentials) {
8531
16.7k
    buffer.insert(components.host_start, "@");
8532
16.7k
    new_difference++;
8533
16.7k
  }
8534
168k
  components.host_end += new_difference;
8535
168k
  components.pathname_start += new_difference;
8536
168k
  if (components.search_start != url_components::omitted) {
8537
0
    components.search_start += new_difference;
8538
0
  }
8539
168k
  if (components.hash_start != url_components::omitted) {
8540
0
    components.hash_start += new_difference;
8541
0
  }
8542
168k
  ADA_ASSERT_TRUE(validate());
8543
168k
}
8544
8545
[[nodiscard]] ada_really_inline uint32_t
8546
145k
url_aggregator::get_pathname_length() const noexcept {
8547
145k
  ada_log("url_aggregator::get_pathname_length");
8548
145k
  uint32_t ending_index = uint32_t(buffer.size());
8549
145k
  if (components.search_start != url_components::omitted) {
8550
0
    ending_index = components.search_start;
8551
145k
  } else if (components.hash_start != url_components::omitted) {
8552
0
    ending_index = components.hash_start;
8553
0
  }
8554
145k
  return ending_index - components.pathname_start;
8555
145k
}
8556
8557
[[nodiscard]] ada_really_inline bool url_aggregator::is_at_path()
8558
330k
    const noexcept {
8559
330k
  return buffer.size() == components.pathname_start;
8560
330k
}
8561
8562
0
inline void url_aggregator::update_base_search(std::string_view input) {
8563
0
  ada_log("url_aggregator::update_base_search ", input);
8564
0
  ADA_ASSERT_TRUE(validate());
8565
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8566
0
  if (input.empty()) {
8567
0
    clear_search();
8568
0
    return;
8569
0
  }
8570
8571
0
  if (input[0] == '?') {
8572
0
    input.remove_prefix(1);
8573
0
  }
8574
8575
0
  if (components.hash_start == url_components::omitted) {
8576
0
    if (components.search_start == url_components::omitted) {
8577
0
      components.search_start = uint32_t(buffer.size());
8578
0
      buffer += "?";
8579
0
    } else {
8580
0
      buffer.resize(components.search_start + 1);
8581
0
    }
8582
8583
0
    buffer.append(input);
8584
0
  } else if (components.search_start != url_components::omitted) {
8585
0
    const uint32_t difference = replace_and_resize(
8586
0
        components.search_start + 1, components.hash_start, input);
8587
0
    components.hash_start += difference;
8588
0
  } else {
8589
0
    components.search_start = components.hash_start;
8590
0
    buffer.insert(components.search_start, input.size() + 1, '?');
8591
0
    if (!input.empty()) {
8592
0
      std::memmove(buffer.data() + components.search_start + 1, input.data(),
8593
0
                   input.size());
8594
0
    }
8595
0
    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
8596
0
  }
8597
8598
0
  ADA_ASSERT_TRUE(validate());
8599
0
}
8600
8601
inline void url_aggregator::update_base_search(
8602
18.3k
    std::string_view input, const uint8_t query_percent_encode_set[]) {
8603
18.3k
  ada_log("url_aggregator::update_base_search ", input,
8604
18.3k
          " with encoding parameter ", to_string(), "\n", to_diagram());
8605
18.3k
  ADA_ASSERT_TRUE(validate());
8606
18.3k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8607
8608
18.3k
  if (components.hash_start == url_components::omitted) {
8609
18.3k
    if (components.search_start == url_components::omitted) {
8610
18.3k
      components.search_start = uint32_t(buffer.size());
8611
18.3k
      buffer += "?";
8612
18.3k
    } else {
8613
0
      buffer.resize(components.search_start + 1);
8614
0
    }
8615
8616
18.3k
    bool encoding_required =
8617
18.3k
        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
8618
    // When encoding_required is false, then buffer is left unchanged, and
8619
    // percent encoding was not deemed required.
8620
18.3k
    if (!encoding_required) {
8621
9.00k
      buffer.append(input);
8622
9.00k
    }
8623
18.3k
  } else {
8624
0
    const size_t idx =
8625
0
        ada::unicode::percent_encode_index(input, query_percent_encode_set);
8626
0
    std::string encoded;
8627
0
    std::string_view replacement = input;
8628
0
    if (idx != input.size()) {
8629
0
      encoded =
8630
0
          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
8631
0
      replacement = encoded;
8632
0
    }
8633
8634
0
    if (components.search_start != url_components::omitted) {
8635
0
      const uint32_t difference = replace_and_resize(
8636
0
          components.search_start + 1, components.hash_start, replacement);
8637
0
      components.hash_start += difference;
8638
0
    } else {
8639
0
      components.search_start = components.hash_start;
8640
0
      buffer.insert(components.search_start, replacement.size() + 1, '?');
8641
0
      if (!replacement.empty()) {
8642
0
        std::memmove(buffer.data() + components.search_start + 1,
8643
0
                     replacement.data(), replacement.size());
8644
0
      }
8645
0
      components.hash_start +=
8646
0
          uint32_t(replacement.size() + 1);  // Do not forget `?`
8647
0
    }
8648
0
  }
8649
8650
18.3k
  ADA_ASSERT_TRUE(validate());
8651
18.3k
}
8652
8653
145k
inline void url_aggregator::update_base_pathname(const std::string_view input) {
8654
145k
  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
8655
145k
          " bytes] \n", to_diagram());
8656
145k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8657
145k
  ADA_ASSERT_TRUE(validate());
8658
8659
145k
  const bool begins_with_dashdash = input.starts_with("//");
8660
145k
  if (!begins_with_dashdash && has_dash_dot()) {
8661
    // We must delete the ./
8662
0
    delete_dash_dot();
8663
0
  }
8664
8665
145k
  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
8666
18
      !has_dash_dot()) {
8667
    // If url's host is null, url does not have an opaque path, url's path's
8668
    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
8669
    // output.
8670
18
    buffer.insert(components.pathname_start, "/.");
8671
18
    components.pathname_start += 2;
8672
18
    if (components.search_start != url_components::omitted) {
8673
0
      components.search_start += 2;
8674
0
    }
8675
18
    if (components.hash_start != url_components::omitted) {
8676
0
      components.hash_start += 2;
8677
0
    }
8678
18
  }
8679
8680
145k
  uint32_t difference = replace_and_resize(
8681
145k
      components.pathname_start,
8682
145k
      components.pathname_start + get_pathname_length(), input);
8683
145k
  if (components.search_start != url_components::omitted) {
8684
0
    components.search_start += difference;
8685
0
  }
8686
145k
  if (components.hash_start != url_components::omitted) {
8687
0
    components.hash_start += difference;
8688
0
  }
8689
145k
  ADA_ASSERT_TRUE(validate());
8690
145k
}
8691
8692
0
inline void url_aggregator::append_base_pathname(const std::string_view input) {
8693
0
  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
8694
0
          "\n", to_diagram());
8695
0
  ADA_ASSERT_TRUE(validate());
8696
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8697
#if ADA_DEVELOPMENT_CHECKS
8698
  // computing the expected password.
8699
  std::string path_expected(get_pathname());
8700
  path_expected.append(input);
8701
#endif  // ADA_DEVELOPMENT_CHECKS
8702
0
  uint32_t ending_index = uint32_t(buffer.size());
8703
0
  if (components.search_start != url_components::omitted) {
8704
0
    ending_index = components.search_start;
8705
0
  } else if (components.hash_start != url_components::omitted) {
8706
0
    ending_index = components.hash_start;
8707
0
  }
8708
0
  buffer.insert(ending_index, input);
8709
8710
0
  if (components.search_start != url_components::omitted) {
8711
0
    components.search_start += uint32_t(input.size());
8712
0
  }
8713
0
  if (components.hash_start != url_components::omitted) {
8714
0
    components.hash_start += uint32_t(input.size());
8715
0
  }
8716
#if ADA_DEVELOPMENT_CHECKS
8717
  std::string path_after = std::string(get_pathname());
8718
  ADA_ASSERT_EQUAL(
8719
      path_expected, path_after,
8720
      "append_base_pathname problem after inserting " + std::string(input));
8721
#endif  // ADA_DEVELOPMENT_CHECKS
8722
0
  ADA_ASSERT_TRUE(validate());
8723
0
}
8724
8725
0
inline void url_aggregator::update_base_username(const std::string_view input) {
8726
0
  ada_log("url_aggregator::update_base_username '", input, "' ", to_string(),
8727
0
          "\n", to_diagram());
8728
0
  ADA_ASSERT_TRUE(validate());
8729
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8730
8731
0
  add_authority_slashes_if_needed();
8732
8733
0
  bool has_password = has_non_empty_password();
8734
0
  bool host_starts_with_at = buffer.size() > components.host_start &&
8735
0
                             buffer[components.host_start] == '@';
8736
0
  uint32_t diff = replace_and_resize(components.protocol_end + 2,
8737
0
                                     components.username_end, input);
8738
8739
0
  components.username_end += diff;
8740
0
  components.host_start += diff;
8741
8742
0
  if (!input.empty() && !host_starts_with_at) {
8743
0
    buffer.insert(components.host_start, "@");
8744
0
    diff++;
8745
0
  } else if (input.empty() && host_starts_with_at && !has_password) {
8746
    // Input is empty, there is no password, and we need to remove "@" from
8747
    // hostname
8748
0
    buffer.erase(components.host_start, 1);
8749
0
    diff--;
8750
0
  }
8751
8752
0
  components.host_end += diff;
8753
0
  components.pathname_start += diff;
8754
0
  if (components.search_start != url_components::omitted) {
8755
0
    components.search_start += diff;
8756
0
  }
8757
0
  if (components.hash_start != url_components::omitted) {
8758
0
    components.hash_start += diff;
8759
0
  }
8760
0
  ADA_ASSERT_TRUE(validate());
8761
0
}
8762
8763
60.4k
inline void url_aggregator::append_base_username(const std::string_view input) {
8764
60.4k
  ada_log("url_aggregator::append_base_username ", input);
8765
60.4k
  ADA_ASSERT_TRUE(validate());
8766
60.4k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8767
#if ADA_DEVELOPMENT_CHECKS
8768
  // computing the expected password.
8769
  std::string username_expected(get_username());
8770
  username_expected.append(input);
8771
#endif  // ADA_DEVELOPMENT_CHECKS
8772
60.4k
  add_authority_slashes_if_needed();
8773
8774
  // If input is empty, do nothing.
8775
60.4k
  if (input.empty()) {
8776
22.8k
    return;
8777
22.8k
  }
8778
8779
37.5k
  uint32_t difference = uint32_t(input.size());
8780
37.5k
  buffer.insert(components.username_end, input);
8781
37.5k
  components.username_end += difference;
8782
37.5k
  components.host_start += difference;
8783
8784
37.5k
  if (buffer[components.host_start] != '@' &&
8785
17.3k
      components.host_start != components.host_end) {
8786
17.3k
    buffer.insert(components.host_start, "@");
8787
17.3k
    difference++;
8788
17.3k
  }
8789
8790
37.5k
  components.host_end += difference;
8791
37.5k
  components.pathname_start += difference;
8792
37.5k
  if (components.search_start != url_components::omitted) {
8793
0
    components.search_start += difference;
8794
0
  }
8795
37.5k
  if (components.hash_start != url_components::omitted) {
8796
0
    components.hash_start += difference;
8797
0
  }
8798
#if ADA_DEVELOPMENT_CHECKS
8799
  std::string username_after(get_username());
8800
  ADA_ASSERT_EQUAL(
8801
      username_expected, username_after,
8802
      "append_base_username problem after inserting " + std::string(input));
8803
#endif  // ADA_DEVELOPMENT_CHECKS
8804
37.5k
  ADA_ASSERT_TRUE(validate());
8805
37.5k
}
8806
8807
0
constexpr void url_aggregator::clear_password() {
8808
0
  ada_log("url_aggregator::clear_password ", to_string());
8809
0
  ADA_ASSERT_TRUE(validate());
8810
0
  if (!has_password()) {
8811
0
    return;
8812
0
  }
8813
8814
0
  uint32_t diff = components.host_start - components.username_end;
8815
0
  buffer.erase(components.username_end, diff);
8816
0
  components.host_start -= diff;
8817
0
  components.host_end -= diff;
8818
0
  components.pathname_start -= diff;
8819
0
  if (components.search_start != url_components::omitted) {
8820
0
    components.search_start -= diff;
8821
0
  }
8822
0
  if (components.hash_start != url_components::omitted) {
8823
0
    components.hash_start -= diff;
8824
0
  }
8825
0
}
8826
8827
0
inline void url_aggregator::update_base_password(const std::string_view input) {
8828
0
  ada_log("url_aggregator::update_base_password ", input);
8829
0
  ADA_ASSERT_TRUE(validate());
8830
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8831
8832
0
  add_authority_slashes_if_needed();
8833
8834
  // TODO: Optimization opportunity. Merge the following removal functions.
8835
0
  if (input.empty()) {
8836
0
    clear_password();
8837
8838
    // Remove username too, if it is empty.
8839
0
    if (!has_non_empty_username()) {
8840
0
      update_base_username("");
8841
0
    }
8842
8843
0
    return;
8844
0
  }
8845
8846
0
  bool password_exists = has_password();
8847
0
  uint32_t difference = uint32_t(input.size());
8848
8849
0
  if (password_exists) {
8850
0
    difference = replace_and_resize(components.username_end + 1,
8851
0
                                    components.host_start, input);
8852
0
  } else {
8853
0
    buffer.insert(components.username_end, input.size() + 1, ':');
8854
0
    std::memmove(buffer.data() + components.username_end + 1, input.data(),
8855
0
                 input.size());
8856
0
    difference++;
8857
0
  }
8858
0
  components.host_start += difference;
8859
8860
  // The following line is required to add "@" to hostname. When updating
8861
  // password if hostname does not start with "@", it is "update_base_password"s
8862
  // responsibility to set it.
8863
0
  if (buffer[components.host_start] != '@') {
8864
0
    buffer.insert(components.host_start, "@");
8865
0
    difference++;
8866
0
  }
8867
8868
0
  components.host_end += difference;
8869
0
  components.pathname_start += difference;
8870
0
  if (components.search_start != url_components::omitted) {
8871
0
    components.search_start += difference;
8872
0
  }
8873
0
  if (components.hash_start != url_components::omitted) {
8874
0
    components.hash_start += difference;
8875
0
  }
8876
0
  ADA_ASSERT_TRUE(validate());
8877
0
}
8878
8879
34.9k
inline void url_aggregator::append_base_password(const std::string_view input) {
8880
34.9k
  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
8881
34.9k
          "\n", to_diagram());
8882
34.9k
  ADA_ASSERT_TRUE(validate());
8883
34.9k
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
8884
#if ADA_DEVELOPMENT_CHECKS
8885
  // computing the expected password.
8886
  std::string password_expected = std::string(get_password());
8887
  password_expected.append(input);
8888
#endif  // ADA_DEVELOPMENT_CHECKS
8889
34.9k
  add_authority_slashes_if_needed();
8890
8891
  // If input is empty, do nothing.
8892
34.9k
  if (input.empty()) {
8893
13.3k
    return;
8894
13.3k
  }
8895
8896
21.6k
  uint32_t difference = uint32_t(input.size());
8897
21.6k
  if (has_password()) {
8898
14.7k
    buffer.insert(components.host_start, input);
8899
14.7k
  } else {
8900
6.93k
    difference++;  // Increment for ":"
8901
6.93k
    buffer.insert(components.username_end, ":");
8902
6.93k
    buffer.insert(components.username_end + 1, input);
8903
6.93k
  }
8904
21.6k
  components.host_start += difference;
8905
8906
  // The following line is required to add "@" to hostname. When updating
8907
  // password if hostname does not start with "@", it is "append_base_password"s
8908
  // responsibility to set it.
8909
21.6k
  if (buffer[components.host_start] != '@') {
8910
5.73k
    buffer.insert(components.host_start, "@");
8911
5.73k
    difference++;
8912
5.73k
  }
8913
8914
21.6k
  components.host_end += difference;
8915
21.6k
  components.pathname_start += difference;
8916
21.6k
  if (components.search_start != url_components::omitted) {
8917
0
    components.search_start += difference;
8918
0
  }
8919
21.6k
  if (components.hash_start != url_components::omitted) {
8920
0
    components.hash_start += difference;
8921
0
  }
8922
#if ADA_DEVELOPMENT_CHECKS
8923
  std::string password_after(get_password());
8924
  ADA_ASSERT_EQUAL(
8925
      password_expected, password_after,
8926
      "append_base_password problem after inserting " + std::string(input));
8927
#endif  // ADA_DEVELOPMENT_CHECKS
8928
21.6k
  ADA_ASSERT_TRUE(validate());
8929
21.6k
}
8930
8931
6.62k
inline void url_aggregator::update_base_port(uint32_t input) {
8932
6.62k
  ada_log("url_aggregator::update_base_port");
8933
6.62k
  ADA_ASSERT_TRUE(validate());
8934
6.62k
  if (input == url_components::omitted) {
8935
0
    clear_port();
8936
0
    return;
8937
0
  }
8938
  // calling std::to_string(input.value()) is unfortunate given that the port
8939
  // value is probably already available as a string.
8940
6.62k
  std::string value = helpers::concat(":", std::to_string(input));
8941
6.62k
  uint32_t difference = uint32_t(value.size());
8942
8943
6.62k
  if (components.port != url_components::omitted) {
8944
0
    difference -= components.pathname_start - components.host_end;
8945
0
    buffer.erase(components.host_end,
8946
0
                 components.pathname_start - components.host_end);
8947
0
  }
8948
8949
6.62k
  buffer.insert(components.host_end, value);
8950
6.62k
  components.pathname_start += difference;
8951
6.62k
  if (components.search_start != url_components::omitted) {
8952
0
    components.search_start += difference;
8953
0
  }
8954
6.62k
  if (components.hash_start != url_components::omitted) {
8955
0
    components.hash_start += difference;
8956
0
  }
8957
6.62k
  components.port = input;
8958
6.62k
  ADA_ASSERT_TRUE(validate());
8959
6.62k
}
8960
8961
20.4k
inline void url_aggregator::clear_port() {
8962
20.4k
  ada_log("url_aggregator::clear_port");
8963
20.4k
  ADA_ASSERT_TRUE(validate());
8964
20.4k
  if (components.port == url_components::omitted) {
8965
20.4k
    return;
8966
20.4k
  }
8967
0
  uint32_t length = components.pathname_start - components.host_end;
8968
0
  buffer.erase(components.host_end, length);
8969
0
  components.pathname_start -= length;
8970
0
  if (components.search_start != url_components::omitted) {
8971
0
    components.search_start -= length;
8972
0
  }
8973
0
  if (components.hash_start != url_components::omitted) {
8974
0
    components.hash_start -= length;
8975
0
  }
8976
0
  components.port = url_components::omitted;
8977
0
  ADA_ASSERT_TRUE(validate());
8978
0
}
8979
8980
0
[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
8981
0
  ada_log("url_aggregator::retrieve_base_port");
8982
0
  return components.port;
8983
0
}
8984
8985
0
inline void url_aggregator::clear_search() {
8986
0
  ada_log("url_aggregator::clear_search");
8987
0
  ADA_ASSERT_TRUE(validate());
8988
0
  if (components.search_start == url_components::omitted) {
8989
0
    return;
8990
0
  }
8991
8992
0
  if (components.hash_start == url_components::omitted) {
8993
0
    buffer.resize(components.search_start);
8994
0
  } else {
8995
0
    buffer.erase(components.search_start,
8996
0
                 components.hash_start - components.search_start);
8997
0
    components.hash_start = components.search_start;
8998
0
  }
8999
9000
0
  components.search_start = url_components::omitted;
9001
9002
#if ADA_DEVELOPMENT_CHECKS
9003
  ADA_ASSERT_EQUAL(get_search(), "",
9004
                   "search should have been cleared on buffer=" + buffer +
9005
                       " with " + components.to_string() + "\n" + to_diagram());
9006
#endif
9007
0
  ADA_ASSERT_TRUE(validate());
9008
0
}
9009
9010
0
inline void url_aggregator::clear_hash() {
9011
0
  ada_log("url_aggregator::clear_hash");
9012
0
  ADA_ASSERT_TRUE(validate());
9013
0
  if (components.hash_start == url_components::omitted) {
9014
0
    return;
9015
0
  }
9016
0
  buffer.resize(components.hash_start);
9017
0
  components.hash_start = url_components::omitted;
9018
9019
#if ADA_DEVELOPMENT_CHECKS
9020
  ADA_ASSERT_EQUAL(get_hash(), "",
9021
                   "hash should have been cleared on buffer=" + buffer +
9022
                       " with " + components.to_string() + "\n" + to_diagram());
9023
#endif
9024
0
  ADA_ASSERT_TRUE(validate());
9025
0
}
9026
9027
260k
constexpr void url_aggregator::clear_pathname() {
9028
260k
  ada_log("url_aggregator::clear_pathname");
9029
260k
  ADA_ASSERT_TRUE(validate());
9030
260k
  uint32_t ending_index = uint32_t(buffer.size());
9031
260k
  if (components.search_start != url_components::omitted) {
9032
0
    ending_index = components.search_start;
9033
260k
  } else if (components.hash_start != url_components::omitted) {
9034
0
    ending_index = components.hash_start;
9035
0
  }
9036
260k
  uint32_t pathname_length = ending_index - components.pathname_start;
9037
260k
  buffer.erase(components.pathname_start, pathname_length);
9038
260k
  uint32_t difference = pathname_length;
9039
260k
  if (components.pathname_start == components.host_end + 2 &&
9040
0
      buffer[components.host_end] == '/' &&
9041
0
      buffer[components.host_end + 1] == '.') {
9042
0
    components.pathname_start -= 2;
9043
0
    buffer.erase(components.host_end, 2);
9044
0
    difference += 2;
9045
0
  }
9046
260k
  if (components.search_start != url_components::omitted) {
9047
0
    components.search_start -= difference;
9048
0
  }
9049
260k
  if (components.hash_start != url_components::omitted) {
9050
0
    components.hash_start -= difference;
9051
0
  }
9052
260k
  ada_log("url_aggregator::clear_pathname completed, running checks...");
9053
#if ADA_DEVELOPMENT_CHECKS
9054
  ADA_ASSERT_EQUAL(get_pathname(), "",
9055
                   "pathname should have been cleared on buffer=" + buffer +
9056
                       " with " + components.to_string() + "\n" + to_diagram());
9057
#endif
9058
260k
  ADA_ASSERT_TRUE(validate());
9059
260k
  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
9060
260k
}
9061
9062
0
constexpr void url_aggregator::clear_hostname() {
9063
0
  ada_log("url_aggregator::clear_hostname");
9064
0
  ADA_ASSERT_TRUE(validate());
9065
0
  if (!has_authority()) {
9066
0
    return;
9067
0
  }
9068
0
  ADA_ASSERT_TRUE(has_authority());
9069
9070
0
  uint32_t hostname_length = components.host_end - components.host_start;
9071
0
  uint32_t start = components.host_start;
9072
9073
  // If hostname starts with "@", we should not remove that character.
9074
0
  if (hostname_length > 0 && buffer[start] == '@') {
9075
0
    start++;
9076
0
    hostname_length--;
9077
0
  }
9078
0
  buffer.erase(start, hostname_length);
9079
0
  components.host_end = start;
9080
0
  components.pathname_start -= hostname_length;
9081
0
  if (components.search_start != url_components::omitted) {
9082
0
    components.search_start -= hostname_length;
9083
0
  }
9084
0
  if (components.hash_start != url_components::omitted) {
9085
0
    components.hash_start -= hostname_length;
9086
0
  }
9087
#if ADA_DEVELOPMENT_CHECKS
9088
  ADA_ASSERT_EQUAL(get_hostname(), "",
9089
                   "hostname should have been cleared on buffer=" + buffer +
9090
                       " with " + components.to_string() + "\n" + to_diagram());
9091
#endif
9092
0
  ADA_ASSERT_TRUE(has_authority());
9093
0
  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
9094
0
                   "hostname should have been cleared on buffer=" + buffer +
9095
0
                       " with " + components.to_string() + "\n" + to_diagram());
9096
0
  ADA_ASSERT_TRUE(validate());
9097
0
}
9098
9099
116k
[[nodiscard]] constexpr bool url_aggregator::has_hash() const noexcept {
9100
116k
  ada_log("url_aggregator::has_hash");
9101
116k
  return components.hash_start != url_components::omitted;
9102
116k
}
9103
9104
116k
[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
9105
116k
  ada_log("url_aggregator::has_search");
9106
116k
  return components.search_start != url_components::omitted;
9107
116k
}
9108
9109
0
constexpr bool url_aggregator::has_credentials() const noexcept {
9110
0
  ada_log("url_aggregator::has_credentials");
9111
0
  return has_non_empty_username() || has_non_empty_password();
9112
0
}
9113
9114
0
constexpr bool url_aggregator::cannot_have_credentials_or_port() const {
9115
0
  ada_log("url_aggregator::cannot_have_credentials_or_port");
9116
0
  return type == ada::scheme::type::FILE ||
9117
0
         components.host_start == components.host_end;
9118
0
}
9119
9120
[[nodiscard]] ada_really_inline const ada::url_components&
9121
0
url_aggregator::get_components() const noexcept {
9122
0
  return components;
9123
0
}
9124
9125
[[nodiscard]] constexpr bool ada::url_aggregator::has_authority()
9126
274k
    const noexcept {
9127
274k
  ada_log("url_aggregator::has_authority");
9128
  // Performance: instead of doing this potentially expensive check, we could
9129
  // have a boolean in the struct.
9130
274k
  return components.protocol_end + 2 <= components.host_start &&
9131
101k
         buffer[components.protocol_end] == '/' &&
9132
101k
         buffer[components.protocol_end + 1] == '/';
9133
274k
}
9134
9135
264k
inline void ada::url_aggregator::add_authority_slashes_if_needed() {
9136
264k
  ada_log("url_aggregator::add_authority_slashes_if_needed");
9137
264k
  ADA_ASSERT_TRUE(validate());
9138
  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
9139
  // to insert
9140
  // `//` initially to the buffer, since it depends on the hostname existence.
9141
264k
  if (has_authority()) {
9142
91.2k
    return;
9143
91.2k
  }
9144
  // Performance: the common case is components.protocol_end == buffer.size()
9145
  // Optimization opportunity: in many cases, the "//" is part of the input and
9146
  // the insert could be fused with another insert.
9147
172k
  buffer.insert(components.protocol_end, "//");
9148
172k
  components.username_end += 2;
9149
172k
  components.host_start += 2;
9150
172k
  components.host_end += 2;
9151
172k
  components.pathname_start += 2;
9152
172k
  if (components.search_start != url_components::omitted) {
9153
0
    components.search_start += 2;
9154
0
  }
9155
172k
  if (components.hash_start != url_components::omitted) {
9156
0
    components.hash_start += 2;
9157
0
  }
9158
172k
  ADA_ASSERT_TRUE(validate());
9159
172k
}
9160
9161
789k
constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
9162
789k
  buffer.reserve(capacity);
9163
789k
}
9164
9165
233k
constexpr bool url_aggregator::has_non_empty_username() const noexcept {
9166
233k
  ada_log("url_aggregator::has_non_empty_username");
9167
233k
  return components.protocol_end + 2 < components.username_end;
9168
233k
}
9169
9170
233k
constexpr bool url_aggregator::has_non_empty_password() const noexcept {
9171
233k
  ada_log("url_aggregator::has_non_empty_password");
9172
233k
  return components.host_start > components.username_end;
9173
233k
}
9174
9175
21.6k
constexpr bool url_aggregator::has_password() const noexcept {
9176
21.6k
  ada_log("url_aggregator::has_password");
9177
  // This function does not care about the length of the password
9178
21.6k
  return components.host_start > components.username_end &&
9179
14.7k
         buffer[components.username_end] == ':';
9180
21.6k
}
9181
9182
0
constexpr bool url_aggregator::has_empty_hostname() const noexcept {
9183
0
  if (!has_hostname()) {
9184
0
    return false;
9185
0
  }
9186
0
  if (components.host_start == components.host_end) {
9187
0
    return true;
9188
0
  }
9189
0
  if (components.host_end > components.host_start + 1) {
9190
0
    return false;
9191
0
  }
9192
0
  return components.username_end != components.host_start;
9193
0
}
9194
9195
0
constexpr bool url_aggregator::has_hostname() const noexcept {
9196
0
  return has_authority();
9197
0
}
9198
9199
0
constexpr bool url_aggregator::has_port() const noexcept {
9200
0
  ada_log("url_aggregator::has_port");
9201
  // A URL cannot have a username/password/port if its host is null or the empty
9202
  // string, or its scheme is "file".
9203
0
  return has_hostname() && components.pathname_start != components.host_end;
9204
0
}
9205
9206
138k
[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
9207
  // If url's host is null, url does not have an opaque path, url's path's size
9208
  // is greater than 1, and url's path[0] is the empty string, then append
9209
  // U+002F (/) followed by U+002E (.) to output.
9210
138k
  ada_log("url_aggregator::has_dash_dot");
9211
#if ADA_DEVELOPMENT_CHECKS
9212
  // If pathname_start and host_end are exactly two characters apart, then we
9213
  // either have a one-digit port such as http://test.com:5?param=1 or else we
9214
  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
9215
  if (components.pathname_start == components.host_end + 2) {
9216
    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
9217
                     buffer[components.host_end + 1] == '.') ||
9218
                    (buffer[components.host_end] == ':' &&
9219
                     checkers::is_digit(buffer[components.host_end + 1])));
9220
  }
9221
  if (components.pathname_start == components.host_end + 2 &&
9222
      buffer[components.host_end] == '/' &&
9223
      buffer[components.host_end + 1] == '.') {
9224
    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
9225
    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
9226
    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
9227
  }
9228
#endif
9229
  // Performance: it should be uncommon for components.pathname_start ==
9230
  // components.host_end + 2 to be true. So we put this check first in the
9231
  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
9232
  // more expensive, but should be uncommon.
9233
138k
  return components.pathname_start == components.host_end + 2 &&
9234
2.23k
         !has_opaque_path && buffer[components.host_end] == '/' &&
9235
0
         buffer[components.host_end + 1] == '.';
9236
138k
}
9237
9238
[[nodiscard]] constexpr std::string_view url_aggregator::get_href()
9239
0
    const noexcept ada_lifetime_bound {
9240
0
  ada_log("url_aggregator::get_href");
9241
0
  return buffer;
9242
0
}
9243
9244
0
[[nodiscard]] constexpr size_t url_aggregator::get_href_size() const noexcept {
9245
0
  return buffer.size();
9246
0
}
9247
9248
ada_really_inline size_t
9249
30.5k
url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
9250
30.5k
  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
9251
30.5k
  if (!view.empty() && view[0] == '-') {
9252
203
    ada_log("parse_port: view[0] == '0' && view.size() > 1");
9253
203
    is_valid = false;
9254
203
    return 0;
9255
203
  }
9256
30.3k
  uint16_t parsed_port{};
9257
30.3k
  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
9258
30.3k
  if (r.ec == std::errc::result_out_of_range) {
9259
835
    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
9260
835
    is_valid = false;
9261
835
    return 0;
9262
835
  }
9263
29.4k
  ada_log("parse_port: ", parsed_port);
9264
29.4k
  const size_t consumed = size_t(r.ptr - view.data());
9265
29.4k
  ada_log("parse_port: consumed ", consumed);
9266
29.4k
  if (check_trailing_content) {
9267
29.4k
    is_valid &=
9268
29.4k
        (consumed == view.size() || view[consumed] == '/' ||
9269
4.72k
         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
9270
29.4k
  }
9271
29.4k
  ada_log("parse_port: is_valid = ", is_valid);
9272
29.4k
  if (is_valid) {
9273
27.0k
    ada_log("parse_port", r.ec == std::errc());
9274
    // scheme_default_port can return 0, and we should allow 0 as a base port.
9275
27.0k
    auto default_port = scheme_default_port();
9276
27.0k
    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
9277
27.0k
                         (default_port != parsed_port);
9278
27.0k
    if (r.ec == std::errc() && is_port_valid) {
9279
6.62k
      update_base_port(parsed_port);
9280
20.4k
    } else {
9281
20.4k
      clear_port();
9282
20.4k
    }
9283
27.0k
  }
9284
29.4k
  return consumed;
9285
30.3k
}
9286
9287
0
constexpr void url_aggregator::set_protocol_as_file() {
9288
0
  ada_log("url_aggregator::set_protocol_as_file ");
9289
0
  ADA_ASSERT_TRUE(validate());
9290
0
  type = ada::scheme::type::FILE;
9291
  // next line could overflow but unsigned arithmetic has well-defined
9292
  // overflows.
9293
0
  uint32_t new_difference = 5 - components.protocol_end;
9294
9295
0
  if (buffer.empty()) {
9296
0
    buffer.append("file:");
9297
0
  } else {
9298
0
    buffer.erase(0, components.protocol_end);
9299
0
    buffer.insert(0, "file:");
9300
0
  }
9301
0
  components.protocol_end = 5;
9302
9303
  // Update the rest of the components.
9304
0
  components.username_end += new_difference;
9305
0
  components.host_start += new_difference;
9306
0
  components.host_end += new_difference;
9307
0
  components.pathname_start += new_difference;
9308
0
  if (components.search_start != url_components::omitted) {
9309
0
    components.search_start += new_difference;
9310
0
  }
9311
0
  if (components.hash_start != url_components::omitted) {
9312
0
    components.hash_start += new_difference;
9313
0
  }
9314
0
  ADA_ASSERT_TRUE(validate());
9315
0
}
9316
9317
0
[[nodiscard]] constexpr bool url_aggregator::validate() const noexcept {
9318
0
  if (!is_valid) {
9319
0
    return true;
9320
0
  }
9321
0
  if (!components.check_offset_consistency()) {
9322
0
    ada_log("url_aggregator::validate inconsistent components \n",
9323
0
            to_diagram());
9324
0
    return false;
9325
0
  }
9326
0
  // We have a credible components struct, but let us investivate more
9327
0
  // carefully:
9328
0
  /**
9329
0
   * https://user:pass@example.com:1234/foo/bar?baz#quux
9330
0
   *       |     |    |          | ^^^^|       |   |
9331
0
   *       |     |    |          | |   |       |   `----- hash_start
9332
0
   *       |     |    |          | |   |       `--------- search_start
9333
0
   *       |     |    |          | |   `----------------- pathname_start
9334
0
   *       |     |    |          | `--------------------- port
9335
0
   *       |     |    |          `----------------------- host_end
9336
0
   *       |     |    `---------------------------------- host_start
9337
0
   *       |     `--------------------------------------- username_end
9338
0
   *       `--------------------------------------------- protocol_end
9339
0
   */
9340
0
  if (components.protocol_end == url_components::omitted) {
9341
0
    ada_log("url_aggregator::validate omitted protocol_end \n", to_diagram());
9342
0
    return false;
9343
0
  }
9344
0
  if (components.username_end == url_components::omitted) {
9345
0
    ada_log("url_aggregator::validate omitted username_end \n", to_diagram());
9346
0
    return false;
9347
0
  }
9348
0
  if (components.host_start == url_components::omitted) {
9349
0
    ada_log("url_aggregator::validate omitted host_start \n", to_diagram());
9350
0
    return false;
9351
0
  }
9352
0
  if (components.host_end == url_components::omitted) {
9353
0
    ada_log("url_aggregator::validate omitted host_end \n", to_diagram());
9354
0
    return false;
9355
0
  }
9356
0
  if (components.pathname_start == url_components::omitted) {
9357
0
    ada_log("url_aggregator::validate omitted pathname_start \n", to_diagram());
9358
0
    return false;
9359
0
  }
9360
0
9361
0
  if (components.protocol_end > buffer.size()) {
9362
0
    ada_log("url_aggregator::validate protocol_end overflow \n", to_diagram());
9363
0
    return false;
9364
0
  }
9365
0
  if (components.username_end > buffer.size()) {
9366
0
    ada_log("url_aggregator::validate username_end overflow \n", to_diagram());
9367
0
    return false;
9368
0
  }
9369
0
  if (components.host_start > buffer.size()) {
9370
0
    ada_log("url_aggregator::validate host_start overflow \n", to_diagram());
9371
0
    return false;
9372
0
  }
9373
0
  if (components.host_end > buffer.size()) {
9374
0
    ada_log("url_aggregator::validate host_end overflow \n", to_diagram());
9375
0
    return false;
9376
0
  }
9377
0
  if (components.pathname_start > buffer.size()) {
9378
0
    ada_log("url_aggregator::validate pathname_start overflow \n",
9379
0
            to_diagram());
9380
0
    return false;
9381
0
  }
9382
0
9383
0
  if (components.protocol_end > 0) {
9384
0
    if (buffer[components.protocol_end - 1] != ':') {
9385
0
      ada_log(
9386
0
          "url_aggregator::validate missing : at the end of the protocol \n",
9387
0
          to_diagram());
9388
0
      return false;
9389
0
    }
9390
0
  }
9391
0
9392
0
  if (components.username_end != buffer.size() &&
9393
0
      components.username_end > components.protocol_end + 2) {
9394
0
    if (buffer[components.username_end] != ':' &&
9395
0
        buffer[components.username_end] != '@') {
9396
0
      ada_log(
9397
0
          "url_aggregator::validate missing : or @ at the end of the username "
9398
0
          "\n",
9399
0
          to_diagram());
9400
0
      return false;
9401
0
    }
9402
0
  }
9403
0
9404
0
  if (components.host_start != buffer.size()) {
9405
0
    if (components.host_start > components.username_end) {
9406
0
      if (buffer[components.host_start] != '@') {
9407
0
        ada_log(
9408
0
            "url_aggregator::validate missing @ at the end of the password \n",
9409
0
            to_diagram());
9410
0
        return false;
9411
0
      }
9412
0
    } else if (components.host_start == components.username_end &&
9413
0
               components.host_end > components.host_start) {
9414
0
      if (components.host_start == components.protocol_end + 2) {
9415
0
        if (buffer[components.protocol_end] != '/' ||
9416
0
            buffer[components.protocol_end + 1] != '/') {
9417
0
          ada_log(
9418
0
              "url_aggregator::validate missing // between protocol and host "
9419
0
              "\n",
9420
0
              to_diagram());
9421
0
          return false;
9422
0
        }
9423
0
      } else {
9424
0
        if (components.host_start > components.protocol_end &&
9425
0
            buffer[components.host_start] != '@') {
9426
0
          ada_log(
9427
0
              "url_aggregator::validate missing @ at the end of the username "
9428
0
              "\n",
9429
0
              to_diagram());
9430
0
          return false;
9431
0
        }
9432
0
      }
9433
0
    } else {
9434
0
      if (components.host_end != components.host_start) {
9435
0
        ada_log("url_aggregator::validate expected omitted host \n",
9436
0
                to_diagram());
9437
0
        return false;
9438
0
      }
9439
0
    }
9440
0
  }
9441
0
  if (components.host_end != buffer.size() &&
9442
0
      components.pathname_start > components.host_end) {
9443
0
    if (components.pathname_start == components.host_end + 2 &&
9444
0
        buffer[components.host_end] == '/' &&
9445
0
        buffer[components.host_end + 1] == '.') {
9446
0
      if (components.pathname_start + 1 >= buffer.size() ||
9447
0
          buffer[components.pathname_start] != '/' ||
9448
0
          buffer[components.pathname_start + 1] != '/') {
9449
0
        ada_log(
9450
0
            "url_aggregator::validate expected the path to begin with // \n",
9451
0
            to_diagram());
9452
0
        return false;
9453
0
      }
9454
0
    } else if (buffer[components.host_end] != ':') {
9455
0
      ada_log("url_aggregator::validate missing : at the port \n",
9456
0
              to_diagram());
9457
0
      return false;
9458
0
    }
9459
0
  }
9460
0
  if (components.pathname_start != buffer.size() &&
9461
0
      components.pathname_start < components.search_start &&
9462
0
      components.pathname_start < components.hash_start && !has_opaque_path) {
9463
0
    if (buffer[components.pathname_start] != '/') {
9464
0
      ada_log("url_aggregator::validate missing / at the path \n",
9465
0
              to_diagram());
9466
0
      return false;
9467
0
    }
9468
0
  }
9469
0
  if (components.search_start != url_components::omitted) {
9470
0
    if (buffer[components.search_start] != '?') {
9471
0
      ada_log("url_aggregator::validate missing ? at the search \n",
9472
0
              to_diagram());
9473
0
      return false;
9474
0
    }
9475
0
  }
9476
0
  if (components.hash_start != url_components::omitted) {
9477
0
    if (buffer[components.hash_start] != '#') {
9478
0
      ada_log("url_aggregator::validate missing # at the hash \n",
9479
0
              to_diagram());
9480
0
      return false;
9481
0
    }
9482
0
  }
9483
0
9484
0
  return true;
9485
0
}
9486
9487
[[nodiscard]] constexpr std::string_view url_aggregator::get_pathname() const
9488
886k
    ada_lifetime_bound {
9489
886k
  ada_log("url_aggregator::get_pathname pathname_start = ",
9490
886k
          components.pathname_start, " buffer.size() = ", buffer.size(),
9491
886k
          " components.search_start = ", components.search_start,
9492
886k
          " components.hash_start = ", components.hash_start);
9493
886k
  auto ending_index = uint32_t(buffer.size());
9494
886k
  if (components.search_start != url_components::omitted) {
9495
21.9k
    ending_index = components.search_start;
9496
864k
  } else if (components.hash_start != url_components::omitted) {
9497
14.9k
    ending_index = components.hash_start;
9498
14.9k
  }
9499
886k
  return helpers::substring(buffer, components.pathname_start, ending_index);
9500
886k
}
9501
9502
inline std::ostream& operator<<(std::ostream& out,
9503
0
                                const ada::url_aggregator& u) {
9504
0
  return out << u.to_string();
9505
0
}
9506
9507
0
void url_aggregator::update_host_to_base_host(const std::string_view input) {
9508
0
  ada_log("url_aggregator::update_host_to_base_host ", input);
9509
0
  ADA_ASSERT_TRUE(validate());
9510
0
  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
9511
0
  if (type != ada::scheme::type::FILE) {
9512
    // Let host be the result of host parsing host_view with url is not special.
9513
0
    if (input.empty() && !is_special()) {
9514
0
      if (has_hostname()) {
9515
0
        clear_hostname();
9516
0
      } else if (has_dash_dot()) {
9517
0
        add_authority_slashes_if_needed();
9518
0
        delete_dash_dot();
9519
0
      }
9520
0
      return;
9521
0
    }
9522
0
  }
9523
0
  update_base_hostname(input);
9524
0
  ADA_ASSERT_TRUE(validate());
9525
0
  return;
9526
0
}
9527
}  // namespace ada
9528
9529
#endif  // ADA_URL_AGGREGATOR_INL_H
9530
/* end file include/ada/url_aggregator-inl.h */
9531
/* begin file include/ada/url_search_params.h */
9532
/**
9533
 * @file url_search_params.h
9534
 * @brief URL query string parameter manipulation.
9535
 *
9536
 * This file provides the `url_search_params` class for parsing, manipulating,
9537
 * and serializing URL query strings. It implements the URLSearchParams API
9538
 * from the WHATWG URL Standard.
9539
 *
9540
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9541
 */
9542
#ifndef ADA_URL_SEARCH_PARAMS_H
9543
#define ADA_URL_SEARCH_PARAMS_H
9544
9545
#include <cstddef>
9546
#include <optional>
9547
#include <string>
9548
#include <string_view>
9549
#include <utility>
9550
#include <vector>
9551
9552
namespace ada {
9553
9554
/**
9555
 * @brief Iterator types for url_search_params iteration.
9556
 */
9557
enum class url_search_params_iter_type {
9558
  KEYS,    /**< Iterate over parameter keys only */
9559
  VALUES,  /**< Iterate over parameter values only */
9560
  ENTRIES, /**< Iterate over key-value pairs */
9561
};
9562
9563
template <typename T, url_search_params_iter_type Type>
9564
struct url_search_params_iter;
9565
9566
/** Type alias for a key-value pair of string views. */
9567
typedef std::pair<std::string_view, std::string_view> key_value_view_pair;
9568
9569
/** Iterator over search parameter keys. */
9570
using url_search_params_keys_iter =
9571
    url_search_params_iter<std::string_view, url_search_params_iter_type::KEYS>;
9572
/** Iterator over search parameter values. */
9573
using url_search_params_values_iter =
9574
    url_search_params_iter<std::string_view,
9575
                           url_search_params_iter_type::VALUES>;
9576
/** Iterator over search parameter key-value pairs. */
9577
using url_search_params_entries_iter =
9578
    url_search_params_iter<key_value_view_pair,
9579
                           url_search_params_iter_type::ENTRIES>;
9580
9581
/**
9582
 * @brief Class for parsing and manipulating URL query strings.
9583
 *
9584
 * The `url_search_params` class provides methods to parse, modify, and
9585
 * serialize URL query parameters (the part after '?' in a URL). It handles
9586
 * percent-encoding and decoding automatically.
9587
 *
9588
 * All string inputs must be valid UTF-8. The caller is responsible for
9589
 * ensuring UTF-8 validity.
9590
 *
9591
 * Construction and `reset` refuse query strings longer than
9592
 * `get_max_input_length()` (the object is left empty). Individual `append` /
9593
 * `set` calls are not length-capped.
9594
 *
9595
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
9596
 */
9597
struct url_search_params {
9598
  url_search_params() = default;
9599
9600
  /**
9601
   * Constructs url_search_params by parsing a query string.
9602
   * @param input A query string (with or without leading '?'). Must be UTF-8.
9603
   *        If longer than `get_max_input_length()`, the object stays empty.
9604
   */
9605
0
  explicit url_search_params(const std::string_view input) {
9606
0
    initialize(input);
9607
0
  }
9608
9609
  url_search_params(const url_search_params& u) = default;
9610
0
  url_search_params(url_search_params&& u) noexcept = default;
9611
  url_search_params& operator=(url_search_params&& u) noexcept = default;
9612
  url_search_params& operator=(const url_search_params& u) = default;
9613
0
  ~url_search_params() = default;
9614
9615
  /**
9616
   * Returns the number of key-value pairs.
9617
   * @return The total count of parameters.
9618
   */
9619
  [[nodiscard]] inline size_t size() const noexcept;
9620
9621
  /**
9622
   * Appends a new key-value pair to the parameter list.
9623
   * @param key The parameter name (must be valid UTF-8).
9624
   * @param value The parameter value (must be valid UTF-8).
9625
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append
9626
   */
9627
  inline void append(std::string_view key, std::string_view value);
9628
9629
  /**
9630
   * Removes all pairs with the given key.
9631
   * @param key The parameter name to remove.
9632
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete
9633
   */
9634
  inline void remove(std::string_view key);
9635
9636
  /**
9637
   * Removes all pairs with the given key and value.
9638
   * @param key The parameter name.
9639
   * @param value The parameter value to match.
9640
   */
9641
  inline void remove(std::string_view key, std::string_view value);
9642
9643
  /**
9644
   * Returns the value of the first pair with the given key.
9645
   * @param key The parameter name to search for.
9646
   * @return The value if found, or std::nullopt if not present.
9647
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
9648
   */
9649
  inline std::optional<std::string_view> get(std::string_view key);
9650
9651
  /**
9652
   * Returns all values for pairs with the given key.
9653
   * @param key The parameter name to search for.
9654
   * @return A vector of all matching values (may be empty).
9655
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
9656
   */
9657
  inline std::vector<std::string> get_all(std::string_view key);
9658
9659
  /**
9660
   * Checks if any pair has the given key.
9661
   * @param key The parameter name to search for.
9662
   * @return `true` if at least one pair has this key.
9663
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
9664
   */
9665
  inline bool has(std::string_view key) noexcept;
9666
9667
  /**
9668
   * Checks if any pair matches the given key and value.
9669
   * @param key The parameter name to search for.
9670
   * @param value The parameter value to match.
9671
   * @return `true` if a matching pair exists.
9672
   */
9673
  inline bool has(std::string_view key, std::string_view value) noexcept;
9674
9675
  /**
9676
   * Sets a parameter value, replacing any existing pairs with the same key.
9677
   * @param key The parameter name (must be valid UTF-8).
9678
   * @param value The parameter value (must be valid UTF-8).
9679
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
9680
   */
9681
  inline void set(std::string_view key, std::string_view value);
9682
9683
  /**
9684
   * Sorts all key-value pairs by their keys using code unit comparison.
9685
   * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
9686
   */
9687
  inline void sort();
9688
9689
  /**
9690
   * Serializes the parameters to a query string (without leading '?').
9691
   * @return The percent-encoded query string.
9692
   * @see https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
9693
   */
9694
  inline std::string to_string() const;
9695
9696
  /**
9697
   * Returns an iterator over all parameter keys.
9698
   * Keys may repeat if there are duplicate parameters.
9699
   * @return An iterator yielding string_view keys.
9700
   * @note The iterator is invalidated if this object is modified.
9701
   */
9702
  inline url_search_params_keys_iter get_keys();
9703
9704
  /**
9705
   * Returns an iterator over all parameter values.
9706
   * @return An iterator yielding string_view values.
9707
   * @note The iterator is invalidated if this object is modified.
9708
   */
9709
  inline url_search_params_values_iter get_values();
9710
9711
  /**
9712
   * Returns an iterator over all key-value pairs.
9713
   * @return An iterator yielding key-value pair views.
9714
   * @note The iterator is invalidated if this object is modified.
9715
   */
9716
  inline url_search_params_entries_iter get_entries();
9717
9718
  /**
9719
   * C++ style conventional iterator support. const only because we
9720
   * do not really want the params to be modified via the iterator.
9721
   */
9722
0
  inline auto begin() const { return params.begin(); }
9723
0
  inline auto end() const { return params.end(); }
9724
0
  inline auto front() const { return params.front(); }
9725
0
  inline auto back() const { return params.back(); }
9726
0
  inline auto operator[](size_t index) const { return params[index]; }
9727
9728
  /**
9729
   * @private
9730
   * Used to reset the search params to a new input.
9731
   * Used primarily for C API.
9732
   * @param input
9733
   */
9734
  void reset(std::string_view input);
9735
9736
 private:
9737
  typedef std::pair<std::string, std::string> key_value_pair;
9738
  std::vector<key_value_pair> params{};
9739
9740
  /**
9741
   * The init parameter must be valid UTF-8.
9742
   * @see https://url.spec.whatwg.org/#concept-urlencoded-parser
9743
   */
9744
  void initialize(std::string_view init);
9745
9746
  template <typename T, url_search_params_iter_type Type>
9747
  friend struct url_search_params_iter;
9748
};  // url_search_params
9749
9750
/**
9751
 * @brief JavaScript-style iterator for url_search_params.
9752
 *
9753
 * Provides a `next()` method that returns successive values until exhausted.
9754
 * This matches the iterator pattern used in the Web Platform.
9755
 *
9756
 * @tparam T The type of value returned by the iterator.
9757
 * @tparam Type The type of iteration (KEYS, VALUES, or ENTRIES).
9758
 *
9759
 * @see https://webidl.spec.whatwg.org/#idl-iterable
9760
 */
9761
template <typename T, url_search_params_iter_type Type>
9762
struct url_search_params_iter {
9763
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()
9764
  url_search_params_iter(const url_search_params_iter& u) = default;
9765
  url_search_params_iter(url_search_params_iter&& u) noexcept = default;
9766
  url_search_params_iter& operator=(url_search_params_iter&& u) noexcept =
9767
      default;
9768
  url_search_params_iter& operator=(const url_search_params_iter& u) = default;
9769
  ~url_search_params_iter() = default;
9770
9771
  /**
9772
   * Returns the next value in the iteration sequence.
9773
   * @return The next value, or std::nullopt if iteration is complete.
9774
   */
9775
  inline std::optional<T> next();
9776
9777
  /**
9778
   * Checks if more values are available.
9779
   * @return `true` if `next()` will return a value, `false` if exhausted.
9780
   */
9781
  inline bool has_next() const;
9782
9783
 private:
9784
  static url_search_params EMPTY;
9785
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&)
9786
9787
  url_search_params& params;
9788
  size_t pos = 0;
9789
9790
  friend struct url_search_params;
9791
};
9792
9793
}  // namespace ada
9794
#endif
9795
/* end file include/ada/url_search_params.h */
9796
/* begin file include/ada/url_search_params-inl.h */
9797
/**
9798
 * @file url_search_params-inl.h
9799
 * @brief Inline declarations for the URL Search Params
9800
 */
9801
#ifndef ADA_URL_SEARCH_PARAMS_INL_H
9802
#define ADA_URL_SEARCH_PARAMS_INL_H
9803
9804
9805
#include <algorithm>
9806
#include <cstddef>
9807
#include <cstdint>
9808
#include <iterator>
9809
#include <optional>
9810
#include <ranges>
9811
#include <string>
9812
#include <string_view>
9813
#include <vector>
9814
9815
namespace ada {
9816
9817
// Declared in implementation.h; used here as a DoS bound on untrusted query
9818
// strings (ada.h includes both headers).
9819
uint32_t get_max_input_length();
9820
9821
// A default, empty url_search_params for use with empty iterators.
9822
template <typename T, ada::url_search_params_iter_type Type>
9823
url_search_params url_search_params_iter<T, Type>::EMPTY;
9824
9825
0
inline void url_search_params::reset(std::string_view input) {
9826
0
  params.clear();
9827
0
  initialize(input);
9828
0
}
9829
9830
0
inline void url_search_params::initialize(std::string_view input) {
9831
0
  if (!input.empty() && input.front() == '?') {
9832
0
    input.remove_prefix(1);
9833
0
  }
9834
0
  if (input.empty()) {
9835
0
    return;
9836
0
  }
9837
  // Refuse overlong query strings (same process-wide cap as URL parsing).
9838
0
  if (input.size() > get_max_input_length()) {
9839
0
    return;
9840
0
  }
9841
9842
0
  params.reserve(size_t(std::count(input.begin(), input.end(), '&')) + 1);
9843
9844
0
  auto process_key_value = [&](const std::string_view current) {
9845
0
    const auto equal = current.find('=');
9846
0
    if (equal == std::string_view::npos) {
9847
0
      params.emplace_back(unicode::form_urlencoded_decode(current), "");
9848
0
    } else {
9849
0
      params.emplace_back(
9850
0
          unicode::form_urlencoded_decode(current.substr(0, equal)),
9851
0
          unicode::form_urlencoded_decode(current.substr(equal + 1)));
9852
0
    }
9853
0
  };
9854
9855
0
  while (!input.empty()) {
9856
0
    const auto ampersand_index = input.find('&');
9857
9858
0
    if (ampersand_index == std::string_view::npos) {
9859
0
      if (!input.empty()) {
9860
0
        process_key_value(input);
9861
0
      }
9862
0
      break;
9863
0
    } else if (ampersand_index != 0) {
9864
0
      process_key_value(input.substr(0, ampersand_index));
9865
0
    }
9866
9867
0
    input.remove_prefix(ampersand_index + 1);
9868
0
  }
9869
0
}
9870
9871
inline void url_search_params::append(const std::string_view key,
9872
0
                                      const std::string_view value) {
9873
0
  params.emplace_back(key, value);
9874
0
}
9875
9876
0
inline size_t url_search_params::size() const noexcept { return params.size(); }
9877
9878
inline std::optional<std::string_view> url_search_params::get(
9879
0
    const std::string_view key) {
9880
0
  auto entry = std::ranges::find_if(
9881
0
      params, [&key](const auto& param) { return param.first == key; });
9882
9883
0
  if (entry == params.end()) {
9884
0
    return std::nullopt;
9885
0
  }
9886
9887
0
  return entry->second;
9888
0
}
9889
9890
inline std::vector<std::string> url_search_params::get_all(
9891
0
    const std::string_view key) {
9892
0
  std::vector<std::string> out{};
9893
9894
0
  for (auto& param : params) {
9895
0
    if (param.first == key) {
9896
0
      out.emplace_back(param.second);
9897
0
    }
9898
0
  }
9899
9900
0
  return out;
9901
0
}
9902
9903
0
inline bool url_search_params::has(const std::string_view key) noexcept {
9904
0
  auto entry = std::ranges::find_if(
9905
0
      params, [&key](const auto& param) { return param.first == key; });
9906
0
  return entry != params.end();
9907
0
}
9908
9909
inline bool url_search_params::has(std::string_view key,
9910
0
                                   std::string_view value) noexcept {
9911
0
  auto entry = std::ranges::find_if(params, [&key, &value](const auto& param) {
9912
0
    return param.first == key && param.second == value;
9913
0
  });
9914
0
  return entry != params.end();
9915
0
}
9916
9917
0
inline std::string url_search_params::to_string() const {
9918
0
  auto character_set = ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE;
9919
0
  std::string out{};
9920
0
  for (size_t i = 0; i < params.size(); i++) {
9921
0
    auto key = ada::unicode::percent_encode(params[i].first, character_set);
9922
0
    auto value = ada::unicode::percent_encode(params[i].second, character_set);
9923
9924
    // Performance optimization: Move this inside percent_encode.
9925
0
    std::ranges::replace(key, ' ', '+');
9926
0
    std::ranges::replace(value, ' ', '+');
9927
9928
0
    if (i != 0) {
9929
0
      out += "&";
9930
0
    }
9931
0
    out.append(key);
9932
0
    out += "=";
9933
0
    out.append(value);
9934
0
  }
9935
0
  return out;
9936
0
}
9937
9938
inline void url_search_params::set(const std::string_view key,
9939
0
                                   const std::string_view value) {
9940
0
  const auto find = [&key](const auto& param) { return param.first == key; };
9941
9942
0
  auto it = std::ranges::find_if(params, find);
9943
9944
0
  if (it == params.end()) {
9945
0
    params.emplace_back(key, value);
9946
0
  } else {
9947
0
    it->second = value;
9948
0
    params.erase(std::remove_if(std::next(it), params.end(), find),
9949
0
                 params.end());
9950
0
  }
9951
0
}
9952
9953
0
inline void url_search_params::remove(const std::string_view key) {
9954
0
  std::erase_if(params,
9955
0
                [&key](const auto& param) { return param.first == key; });
9956
0
}
9957
9958
inline void url_search_params::remove(const std::string_view key,
9959
0
                                      const std::string_view value) {
9960
0
  std::erase_if(params, [&key, &value](const auto& param) {
9961
0
    return param.first == key && param.second == value;
9962
0
  });
9963
0
}
9964
9965
0
inline void url_search_params::sort() {
9966
  // Keys are expected to be valid UTF-8, but percent_decode can produce
9967
  // arbitrary byte sequences. Handle truncated/invalid sequences gracefully.
9968
0
  std::ranges::stable_sort(params, [](const key_value_pair& lhs,
9969
0
                                      const key_value_pair& rhs) {
9970
0
    size_t i = 0, j = 0;
9971
0
    uint32_t low_surrogate1 = 0, low_surrogate2 = 0;
9972
0
    while ((i < lhs.first.size() || low_surrogate1 != 0) &&
9973
0
           (j < rhs.first.size() || low_surrogate2 != 0)) {
9974
0
      uint32_t codePoint1 = 0, codePoint2 = 0;
9975
9976
0
      if (low_surrogate1 != 0) {
9977
0
        codePoint1 = low_surrogate1;
9978
0
        low_surrogate1 = 0;
9979
0
      } else {
9980
0
        uint8_t c1 = uint8_t(lhs.first[i]);
9981
0
        if (c1 > 0x7F && c1 <= 0xDF && i + 1 < lhs.first.size()) {
9982
0
          codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F);
9983
0
          i += 2;
9984
0
        } else if (c1 > 0xDF && c1 <= 0xEF && i + 2 < lhs.first.size()) {
9985
0
          codePoint1 = ((c1 & 0x0F) << 12) |
9986
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) |
9987
0
                       (uint8_t(lhs.first[i + 2]) & 0x3F);
9988
0
          i += 3;
9989
0
        } else if (c1 > 0xEF && c1 <= 0xF7 && i + 3 < lhs.first.size()) {
9990
0
          codePoint1 = ((c1 & 0x07) << 18) |
9991
0
                       ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) |
9992
0
                       ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) |
9993
0
                       (uint8_t(lhs.first[i + 3]) & 0x3F);
9994
0
          i += 4;
9995
9996
0
          codePoint1 -= 0x10000;
9997
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10));
9998
0
          low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF));
9999
0
          codePoint1 = high_surrogate;
10000
0
        } else {
10001
          // ASCII (c1 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10002
0
          codePoint1 = c1;
10003
0
          i++;
10004
0
        }
10005
0
      }
10006
10007
0
      if (low_surrogate2 != 0) {
10008
0
        codePoint2 = low_surrogate2;
10009
0
        low_surrogate2 = 0;
10010
0
      } else {
10011
0
        uint8_t c2 = uint8_t(rhs.first[j]);
10012
0
        if (c2 > 0x7F && c2 <= 0xDF && j + 1 < rhs.first.size()) {
10013
0
          codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F);
10014
0
          j += 2;
10015
0
        } else if (c2 > 0xDF && c2 <= 0xEF && j + 2 < rhs.first.size()) {
10016
0
          codePoint2 = ((c2 & 0x0F) << 12) |
10017
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) |
10018
0
                       (uint8_t(rhs.first[j + 2]) & 0x3F);
10019
0
          j += 3;
10020
0
        } else if (c2 > 0xEF && c2 <= 0xF7 && j + 3 < rhs.first.size()) {
10021
0
          codePoint2 = ((c2 & 0x07) << 18) |
10022
0
                       ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) |
10023
0
                       ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) |
10024
0
                       (uint8_t(rhs.first[j + 3]) & 0x3F);
10025
0
          j += 4;
10026
0
          codePoint2 -= 0x10000;
10027
0
          uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10));
10028
0
          low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF));
10029
0
          codePoint2 = high_surrogate;
10030
0
        } else {
10031
          // ASCII (c2 <= 0x7F) or truncated/invalid UTF-8: treat as raw byte
10032
0
          codePoint2 = c2;
10033
0
          j++;
10034
0
        }
10035
0
      }
10036
10037
0
      if (codePoint1 != codePoint2) {
10038
0
        return (codePoint1 < codePoint2);
10039
0
      }
10040
0
    }
10041
0
    return (j < rhs.first.size() || low_surrogate2 != 0);
10042
0
  });
10043
0
}
10044
10045
0
inline url_search_params_keys_iter url_search_params::get_keys() {
10046
0
  return url_search_params_keys_iter(*this);
10047
0
}
10048
10049
/**
10050
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10051
 */
10052
0
inline url_search_params_values_iter url_search_params::get_values() {
10053
0
  return url_search_params_values_iter(*this);
10054
0
}
10055
10056
/**
10057
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
10058
 */
10059
0
inline url_search_params_entries_iter url_search_params::get_entries() {
10060
0
  return url_search_params_entries_iter(*this);
10061
0
}
10062
10063
template <typename T, url_search_params_iter_type Type>
10064
0
inline bool url_search_params_iter<T, Type>::has_next() const {
10065
0
  return pos < params.params.size();
10066
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
10067
10068
template <>
10069
0
inline std::optional<std::string_view> url_search_params_keys_iter::next() {
10070
0
  if (!has_next()) {
10071
0
    return std::nullopt;
10072
0
  }
10073
0
  return params.params[pos++].first;
10074
0
}
10075
10076
template <>
10077
0
inline std::optional<std::string_view> url_search_params_values_iter::next() {
10078
0
  if (!has_next()) {
10079
0
    return std::nullopt;
10080
0
  }
10081
0
  return params.params[pos++].second;
10082
0
}
10083
10084
template <>
10085
inline std::optional<key_value_view_pair>
10086
0
url_search_params_entries_iter::next() {
10087
0
  if (!has_next()) {
10088
0
    return std::nullopt;
10089
0
  }
10090
0
  return params.params[pos++];
10091
0
}
10092
10093
}  // namespace ada
10094
10095
#endif  // ADA_URL_SEARCH_PARAMS_INL_H
10096
/* end file include/ada/url_search_params-inl.h */
10097
10098
/* begin file include/ada/url_pattern-inl.h */
10099
/**
10100
 * @file url_pattern-inl.h
10101
 * @brief Declaration for the URLPattern inline functions.
10102
 */
10103
#ifndef ADA_URL_PATTERN_INL_H
10104
#define ADA_URL_PATTERN_INL_H
10105
10106
10107
#include <algorithm>
10108
#include <cstddef>
10109
#include <optional>
10110
#include <string>
10111
#include <string_view>
10112
#include <utility>
10113
#include <variant>
10114
#include <vector>
10115
10116
#if ADA_INCLUDE_URL_PATTERN
10117
namespace ada {
10118
10119
0
inline bool url_pattern_init::operator==(const url_pattern_init& other) const {
10120
0
  return protocol == other.protocol && username == other.username &&
10121
0
         password == other.password && hostname == other.hostname &&
10122
0
         port == other.port && search == other.search && hash == other.hash &&
10123
0
         pathname == other.pathname;
10124
0
}
10125
10126
inline bool url_pattern_component_result::operator==(
10127
0
    const url_pattern_component_result& other) const {
10128
0
  return input == other.input && groups == other.groups;
10129
0
}
10130
10131
template <url_pattern_regex::regex_concept regex_provider>
10132
url_pattern_component_result
10133
url_pattern_component<regex_provider>::create_component_match_result(
10134
    std::string&& input,
10135
444k
    std::vector<std::optional<std::string>>&& exec_result) {
10136
  // Let result be a new URLPatternComponentResult.
10137
  // Set result["input"] to input.
10138
  // Let groups be a record<USVString, (USVString or undefined)>.
10139
444k
  auto result =
10140
444k
      url_pattern_component_result{.input = std::move(input), .groups = {}};
10141
10142
  // We explicitly start iterating from 0 even though the spec
10143
  // says we should start from 1. This case is handled by the
10144
  // std_regex_provider which removes the full match from index 0.
10145
  // Use min() to guard against potential mismatches between
10146
  // exec_result size and group_name_list size.
10147
444k
  const size_t size = std::min(exec_result.size(), group_name_list.size());
10148
444k
  result.groups.reserve(size);
10149
882k
  for (size_t index = 0; index < size; index++) {
10150
437k
    result.groups.emplace(group_name_list[index],
10151
437k
                          std::move(exec_result[index]));
10152
437k
  }
10153
444k
  return result;
10154
444k
}
10155
10156
template <url_pattern_regex::regex_concept regex_provider>
10157
std::string_view url_pattern<regex_provider>::get_protocol() const
10158
129k
    ada_lifetime_bound {
10159
  // Return this's associated URL pattern's protocol component's pattern string.
10160
129k
  return protocol_component.pattern;
10161
129k
}
10162
template <url_pattern_regex::regex_concept regex_provider>
10163
std::string_view url_pattern<regex_provider>::get_username() const
10164
129k
    ada_lifetime_bound {
10165
  // Return this's associated URL pattern's username component's pattern string.
10166
129k
  return username_component.pattern;
10167
129k
}
10168
template <url_pattern_regex::regex_concept regex_provider>
10169
std::string_view url_pattern<regex_provider>::get_password() const
10170
129k
    ada_lifetime_bound {
10171
  // Return this's associated URL pattern's password component's pattern string.
10172
129k
  return password_component.pattern;
10173
129k
}
10174
template <url_pattern_regex::regex_concept regex_provider>
10175
std::string_view url_pattern<regex_provider>::get_hostname() const
10176
129k
    ada_lifetime_bound {
10177
  // Return this's associated URL pattern's hostname component's pattern string.
10178
129k
  return hostname_component.pattern;
10179
129k
}
10180
template <url_pattern_regex::regex_concept regex_provider>
10181
std::string_view url_pattern<regex_provider>::get_port() const
10182
129k
    ada_lifetime_bound {
10183
  // Return this's associated URL pattern's port component's pattern string.
10184
129k
  return port_component.pattern;
10185
129k
}
10186
template <url_pattern_regex::regex_concept regex_provider>
10187
std::string_view url_pattern<regex_provider>::get_pathname() const
10188
129k
    ada_lifetime_bound {
10189
  // Return this's associated URL pattern's pathname component's pattern string.
10190
129k
  return pathname_component.pattern;
10191
129k
}
10192
template <url_pattern_regex::regex_concept regex_provider>
10193
std::string_view url_pattern<regex_provider>::get_search() const
10194
129k
    ada_lifetime_bound {
10195
  // Return this's associated URL pattern's search component's pattern string.
10196
129k
  return search_component.pattern;
10197
129k
}
10198
template <url_pattern_regex::regex_concept regex_provider>
10199
std::string_view url_pattern<regex_provider>::get_hash() const
10200
129k
    ada_lifetime_bound {
10201
  // Return this's associated URL pattern's hash component's pattern string.
10202
129k
  return hash_component.pattern;
10203
129k
}
10204
template <url_pattern_regex::regex_concept regex_provider>
10205
129k
bool url_pattern<regex_provider>::ignore_case() const {
10206
129k
  return ignore_case_;
10207
129k
}
10208
template <url_pattern_regex::regex_concept regex_provider>
10209
129k
bool url_pattern<regex_provider>::has_regexp_groups() const {
10210
  // If this's associated URL pattern's has regexp groups, then return true.
10211
129k
  return protocol_component.has_regexp_groups ||
10212
129k
         username_component.has_regexp_groups ||
10213
129k
         password_component.has_regexp_groups ||
10214
129k
         hostname_component.has_regexp_groups ||
10215
128k
         port_component.has_regexp_groups ||
10216
128k
         pathname_component.has_regexp_groups ||
10217
128k
         search_component.has_regexp_groups || hash_component.has_regexp_groups;
10218
129k
}
10219
10220
853k
inline bool url_pattern_part::is_regexp() const noexcept {
10221
853k
  return type == url_pattern_part_type::REGEXP;
10222
853k
}
10223
10224
inline std::string_view url_pattern_compile_component_options::get_delimiter()
10225
1.11M
    const {
10226
1.11M
  if (delimiter) {
10227
241k
    return {&delimiter.value(), 1};
10228
241k
  }
10229
870k
  return {};
10230
1.11M
}
10231
10232
inline std::string_view url_pattern_compile_component_options::get_prefix()
10233
102k
    const {
10234
102k
  if (prefix) {
10235
94.5k
    return {&prefix.value(), 1};
10236
94.5k
  }
10237
8.22k
  return {};
10238
102k
}
10239
10240
template <url_pattern_regex::regex_concept regex_provider>
10241
template <url_pattern_encoding_callback F>
10242
tl::expected<url_pattern_component<regex_provider>, errors>
10243
url_pattern_component<regex_provider>::compile(
10244
    std::string_view input, F& encoding_callback,
10245
1.11M
    url_pattern_compile_component_options& options) {
10246
1.11M
  ada_log("url_pattern_component::compile input: ", input);
10247
  // Let part list be the result of running parse a pattern string given input,
10248
  // options, and encoding callback.
10249
1.11M
  auto part_list = url_pattern_helpers::parse_pattern_string(input, options,
10250
1.11M
                                                             encoding_callback);
10251
10252
1.11M
  if (!part_list) {
10253
7.11k
    ada_log("parse_pattern_string failed");
10254
7.11k
    return tl::unexpected(part_list.error());
10255
7.11k
  }
10256
10257
  // Detect pattern type early to potentially skip expensive regex compilation
10258
1.10M
  const auto has_regexp = [](const auto& part) { return part.is_regexp(); };
10259
1.10M
  const bool has_regexp_groups = std::ranges::any_of(*part_list, has_regexp);
10260
10261
1.10M
  url_pattern_component_type component_type =
10262
1.10M
      url_pattern_component_type::REGEXP;
10263
1.10M
  std::string exact_match_value{};
10264
10265
1.10M
  if (part_list->empty()) {
10266
262k
    component_type = url_pattern_component_type::EMPTY;
10267
840k
  } else if (part_list->size() == 1) {
10268
835k
    const auto& part = (*part_list)[0];
10269
835k
    if (part.type == url_pattern_part_type::FIXED_TEXT &&
10270
255k
        part.modifier == url_pattern_part_modifier::none &&
10271
255k
        !options.ignore_case) {
10272
255k
      component_type = url_pattern_component_type::EXACT_MATCH;
10273
255k
      exact_match_value = part.value;
10274
580k
    } else if (part.type == url_pattern_part_type::FULL_WILDCARD &&
10275
578k
               part.modifier == url_pattern_part_modifier::none &&
10276
577k
               part.prefix.empty() && part.suffix.empty()) {
10277
532k
      component_type = url_pattern_component_type::FULL_WILDCARD;
10278
532k
    }
10279
835k
  }
10280
10281
  // For simple patterns, skip regex generation and compilation entirely
10282
1.10M
  if (component_type != url_pattern_component_type::REGEXP) {
10283
1.05M
    auto pattern_string =
10284
1.05M
        url_pattern_helpers::generate_pattern_string(*part_list, options);
10285
    // For FULL_WILDCARD, we need the group name from
10286
    // generate_regular_expression
10287
1.05M
    std::vector<std::string> name_list;
10288
1.05M
    if (component_type == url_pattern_component_type::FULL_WILDCARD &&
10289
532k
        !part_list->empty()) {
10290
532k
      name_list.push_back((*part_list)[0].name);
10291
532k
    }
10292
1.05M
    return url_pattern_component<regex_provider>(
10293
1.05M
        std::move(pattern_string), typename regex_provider::regex_type{},
10294
1.05M
        std::move(name_list), has_regexp_groups, component_type,
10295
1.05M
        std::move(exact_match_value));
10296
1.05M
  }
10297
10298
  // Generate regex for complex patterns
10299
52.2k
  auto [regular_expression_string, name_list] =
10300
52.2k
      url_pattern_helpers::generate_regular_expression_and_name_list(*part_list,
10301
52.2k
                                                                     options);
10302
52.2k
  auto pattern_string =
10303
52.2k
      url_pattern_helpers::generate_pattern_string(*part_list, options);
10304
10305
52.2k
  std::optional<typename regex_provider::regex_type> regular_expression =
10306
52.2k
      regex_provider::create_instance(regular_expression_string,
10307
52.2k
                                      options.ignore_case);
10308
52.2k
  if (!regular_expression) {
10309
1.17k
    return tl::unexpected(errors::type_error);
10310
1.17k
  }
10311
10312
51.0k
  return url_pattern_component<regex_provider>(
10313
51.0k
      std::move(pattern_string), std::move(*regular_expression),
10314
51.0k
      std::move(name_list), has_regexp_groups, component_type,
10315
51.0k
      std::move(exact_match_value));
10316
52.2k
}
10317
10318
template <url_pattern_regex::regex_concept regex_provider>
10319
bool url_pattern_component<regex_provider>::fast_test(
10320
891k
    std::string_view input) const {
10321
  // Fast path for simple patterns - avoid regex evaluation
10322
  // Using if-else for better branch prediction on common cases
10323
891k
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10324
522k
    return true;
10325
522k
  }
10326
369k
  if (type == url_pattern_component_type::EXACT_MATCH) {
10327
212k
    return input == exact_match_value;
10328
212k
  }
10329
157k
  if (type == url_pattern_component_type::EMPTY) {
10330
149k
    return input.empty();
10331
149k
  }
10332
  // type == REGEXP
10333
7.89k
  return regex_provider::regex_match(input, regexp);
10334
157k
}
10335
10336
template <url_pattern_regex::regex_concept regex_provider>
10337
std::optional<std::vector<std::optional<std::string>>>
10338
url_pattern_component<regex_provider>::fast_match(
10339
959k
    std::string_view input) const {
10340
  // Handle each type directly without redundant checks
10341
959k
  if (type == url_pattern_component_type::FULL_WILDCARD) {
10342
    // FULL_WILDCARD always matches - capture the input (even if empty)
10343
    // If there's no group name, return empty groups
10344
567k
    if (group_name_list.empty()) {
10345
0
      return std::vector<std::optional<std::string>>{};
10346
0
    }
10347
    // Capture the matched input (including empty strings)
10348
567k
    return std::vector<std::optional<std::string>>{std::string(input)};
10349
567k
  }
10350
392k
  if (type == url_pattern_component_type::EXACT_MATCH) {
10351
234k
    if (input == exact_match_value) {
10352
69.2k
      return std::vector<std::optional<std::string>>{};
10353
69.2k
    }
10354
165k
    return std::nullopt;
10355
234k
  }
10356
157k
  if (type == url_pattern_component_type::EMPTY) {
10357
149k
    if (input.empty()) {
10358
128k
      return std::vector<std::optional<std::string>>{};
10359
128k
    }
10360
21.4k
    return std::nullopt;
10361
149k
  }
10362
  // type == REGEXP - use regex
10363
8.08k
  return regex_provider::regex_search(input, regexp);
10364
157k
}
10365
10366
template <url_pattern_regex::regex_concept regex_provider>
10367
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::exec(
10368
388k
    const url_pattern_input& input, const std::string_view* base_url) {
10369
  // Return the result of match given this's associated URL pattern, input, and
10370
  // baseURL if given.
10371
388k
  return match(input, base_url);
10372
388k
}
10373
10374
template <url_pattern_regex::regex_concept regex_provider>
10375
bool url_pattern<regex_provider>::test_components(
10376
    std::string_view protocol, std::string_view username,
10377
    std::string_view password, std::string_view hostname, std::string_view port,
10378
    std::string_view pathname, std::string_view search,
10379
246k
    std::string_view hash) const {
10380
246k
  return protocol_component.fast_test(protocol) &&
10381
126k
         username_component.fast_test(username) &&
10382
125k
         password_component.fast_test(password) &&
10383
124k
         hostname_component.fast_test(hostname) &&
10384
79.0k
         port_component.fast_test(port) &&
10385
78.7k
         pathname_component.fast_test(pathname) &&
10386
55.6k
         search_component.fast_test(search) && hash_component.fast_test(hash);
10387
246k
}
10388
10389
template <url_pattern_regex::regex_concept regex_provider>
10390
result<bool> url_pattern<regex_provider>::test(
10391
388k
    const url_pattern_input& input, const std::string_view* base_url_string) {
10392
  // If input is a URLPatternInit
10393
388k
  if (std::holds_alternative<url_pattern_init>(input)) {
10394
129k
    if (base_url_string) {
10395
0
      return tl::unexpected(errors::type_error);
10396
0
    }
10397
10398
129k
    std::string protocol{}, username{}, password{}, hostname{};
10399
129k
    std::string port{}, pathname{}, search{}, hash{};
10400
10401
129k
    auto apply_result = url_pattern_init::process(
10402
129k
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10403
129k
        protocol, username, password, hostname, port, pathname, search, hash);
10404
10405
129k
    if (!apply_result) {
10406
339
      return false;
10407
339
    }
10408
10409
129k
    std::string_view search_view = *apply_result->search;
10410
129k
    if (search_view.starts_with("?")) {
10411
0
      search_view.remove_prefix(1);
10412
0
    }
10413
10414
129k
    return test_components(*apply_result->protocol, *apply_result->username,
10415
129k
                           *apply_result->password, *apply_result->hostname,
10416
129k
                           *apply_result->port, *apply_result->pathname,
10417
129k
                           search_view, *apply_result->hash);
10418
129k
  }
10419
10420
  // URL string input path.
10421
  // Let baseURL be null. A default-constructed result holds a value (an
10422
  // empty URL), and a relative input would then resolve against it. Start
10423
  // from an error so "no base given" means no value.
10424
259k
  result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10425
259k
  if (base_url_string) {
10426
129k
    base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10427
129k
    if (!base_url) {
10428
0
      return false;
10429
0
    }
10430
129k
  }
10431
10432
259k
  auto url =
10433
259k
      ada::parse<url_aggregator>(std::get<std::string_view>(input),
10434
259k
                                 base_url.has_value() ? &*base_url : nullptr);
10435
259k
  if (!url) {
10436
181k
    return false;
10437
181k
  }
10438
10439
  // Extract components as string_view
10440
77.9k
  auto protocol_view = url->get_protocol();
10441
77.9k
  if (protocol_view.ends_with(":")) {
10442
77.9k
    protocol_view.remove_suffix(1);
10443
77.9k
  }
10444
10445
77.9k
  auto search_view = url->get_search();
10446
77.9k
  if (search_view.starts_with("?")) {
10447
6.29k
    search_view.remove_prefix(1);
10448
6.29k
  }
10449
10450
77.9k
  auto hash_view = url->get_hash();
10451
77.9k
  if (hash_view.starts_with("#")) {
10452
5.86k
    hash_view.remove_prefix(1);
10453
5.86k
  }
10454
10455
77.9k
  return test_components(protocol_view, url->get_username(),
10456
77.9k
                         url->get_password(), url->get_hostname(),
10457
77.9k
                         url->get_port(), url->get_pathname(), search_view,
10458
77.9k
                         hash_view);
10459
259k
}
10460
10461
template <url_pattern_regex::regex_concept regex_provider>
10462
result<std::optional<url_pattern_result>> url_pattern<regex_provider>::match(
10463
518k
    const url_pattern_input& input, const std::string_view* base_url_string) {
10464
518k
  std::string protocol{};
10465
518k
  std::string username{};
10466
518k
  std::string password{};
10467
518k
  std::string hostname{};
10468
518k
  std::string port{};
10469
518k
  std::string pathname{};
10470
518k
  std::string search{};
10471
518k
  std::string hash{};
10472
10473
  // Let inputs be an empty list.
10474
  // Append input to inputs.
10475
518k
  std::vector inputs{input};
10476
10477
  // If input is a URLPatternInit then:
10478
518k
  if (std::holds_alternative<url_pattern_init>(input)) {
10479
129k
    ada_log(
10480
129k
        "url_pattern::match called with url_pattern_init and base_url_string=",
10481
129k
        base_url_string);
10482
    // If baseURLString was given, throw a TypeError.
10483
129k
    if (base_url_string) {
10484
0
      ada_log("failed to match because base_url_string was given");
10485
0
      return tl::unexpected(errors::type_error);
10486
0
    }
10487
10488
    // Let applyResult be the result of process a URLPatternInit given input,
10489
    // "url", protocol, username, password, hostname, port, pathname, search,
10490
    // and hash.
10491
129k
    auto apply_result = url_pattern_init::process(
10492
129k
        std::get<url_pattern_init>(input), url_pattern_init::process_type::url,
10493
129k
        protocol, username, password, hostname, port, pathname, search, hash);
10494
10495
    // If this throws an exception, catch it, and return null.
10496
129k
    if (!apply_result.has_value()) {
10497
339
      ada_log("match returned std::nullopt because process threw");
10498
339
      return std::nullopt;
10499
339
    }
10500
10501
    // Set protocol to applyResult["protocol"].
10502
129k
    ADA_ASSERT_TRUE(apply_result->protocol.has_value());
10503
129k
    protocol = std::move(apply_result->protocol.value());
10504
10505
    // Set username to applyResult["username"].
10506
129k
    ADA_ASSERT_TRUE(apply_result->username.has_value());
10507
129k
    username = std::move(apply_result->username.value());
10508
10509
    // Set password to applyResult["password"].
10510
129k
    ADA_ASSERT_TRUE(apply_result->password.has_value());
10511
129k
    password = std::move(apply_result->password.value());
10512
10513
    // Set hostname to applyResult["hostname"].
10514
129k
    ADA_ASSERT_TRUE(apply_result->hostname.has_value());
10515
129k
    hostname = std::move(apply_result->hostname.value());
10516
10517
    // Set port to applyResult["port"].
10518
129k
    ADA_ASSERT_TRUE(apply_result->port.has_value());
10519
129k
    port = std::move(apply_result->port.value());
10520
10521
    // Set pathname to applyResult["pathname"].
10522
129k
    ADA_ASSERT_TRUE(apply_result->pathname.has_value());
10523
129k
    pathname = std::move(apply_result->pathname.value());
10524
10525
    // Set search to applyResult["search"].
10526
129k
    ADA_ASSERT_TRUE(apply_result->search.has_value());
10527
129k
    if (apply_result->search->starts_with("?")) {
10528
0
      search = apply_result->search->substr(1);
10529
129k
    } else {
10530
129k
      search = std::move(apply_result->search.value());
10531
129k
    }
10532
10533
    // Set hash to applyResult["hash"].
10534
129k
    ADA_ASSERT_TRUE(apply_result->hash.has_value());
10535
129k
    ADA_ASSERT_TRUE(!apply_result->hash->starts_with("#"));
10536
129k
    hash = std::move(apply_result->hash.value());
10537
388k
  } else {
10538
388k
    ADA_ASSERT_TRUE(std::holds_alternative<std::string_view>(input));
10539
10540
    // Let baseURL be null. A default-constructed result holds a value (an
10541
    // empty URL), and a relative input would then resolve against it. Start
10542
    // from an error so "no base given" means no value.
10543
388k
    result<url_aggregator> base_url = tl::unexpected(errors::type_error);
10544
10545
    // If baseURLString was given, then:
10546
388k
    if (base_url_string) {
10547
      // Let baseURL be the result of parsing baseURLString.
10548
129k
      base_url = ada::parse<url_aggregator>(*base_url_string, nullptr);
10549
10550
      // If baseURL is failure, return null.
10551
129k
      if (!base_url) {
10552
0
        ada_log("match returned std::nullopt because failed to parse base_url=",
10553
0
                *base_url_string);
10554
0
        return std::nullopt;
10555
0
      }
10556
10557
      // Append baseURLString to inputs.
10558
129k
      inputs.emplace_back(*base_url_string);
10559
129k
    }
10560
10561
388k
    url_aggregator* base_url_value =
10562
388k
        base_url.has_value() ? &*base_url : nullptr;
10563
10564
    // Set url to the result of parsing input given baseURL.
10565
388k
    auto url = ada::parse<url_aggregator>(std::get<std::string_view>(input),
10566
388k
                                          base_url_value);
10567
10568
    // If url is failure, return null.
10569
388k
    if (!url) {
10570
272k
      ada_log("match returned std::nullopt because url failed");
10571
272k
      return std::nullopt;
10572
272k
    }
10573
10574
    // Set protocol to url's scheme.
10575
    // IMPORTANT: Not documented on the URLPattern spec, but protocol suffix ':'
10576
    // is removed. Similar work was done on workerd:
10577
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2038
10578
116k
    protocol = url->get_protocol().substr(0, url->get_protocol().size() - 1);
10579
    // Set username to url's username.
10580
116k
    username = url->get_username();
10581
    // Set password to url's password.
10582
116k
    password = url->get_password();
10583
    // Set hostname to url's host, serialized, or the empty string if the value
10584
    // is null.
10585
116k
    hostname = url->get_hostname();
10586
    // Set port to url's port, serialized, or the empty string if the value is
10587
    // null.
10588
116k
    port = url->get_port();
10589
    // Set pathname to the result of URL path serializing url.
10590
116k
    pathname = url->get_pathname();
10591
    // Set search to url's query or the empty string if the value is null.
10592
    // IMPORTANT: Not documented on the URLPattern spec, but search prefix '?'
10593
    // is removed. Similar work was done on workerd:
10594
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2232
10595
116k
    if (url->has_search()) {
10596
10.9k
      auto view = url->get_search();
10597
10.9k
      search = view.starts_with("?") ? url->get_search().substr(1) : view;
10598
10.9k
    }
10599
    // Set hash to url's fragment or the empty string if the value is null.
10600
    // IMPORTANT: Not documented on the URLPattern spec, but hash prefix '#' is
10601
    // removed. Similar work was done on workerd:
10602
    // https://github.com/cloudflare/workerd/blob/8620d14012513a6ce04d079e401d3becac3c67bd/src/workerd/jsg/url.c%2B%2B#L2242
10603
116k
    if (url->has_hash()) {
10604
9.54k
      auto view = url->get_hash();
10605
9.54k
      hash = view.starts_with("#") ? url->get_hash().substr(1) : view;
10606
9.54k
    }
10607
116k
  }
10608
10609
  // Use fast_match which skips regex for simple patterns (EMPTY, EXACT_MATCH,
10610
  // FULL_WILDCARD) and only falls back to regex for complex REGEXP patterns.
10611
10612
  // Let protocolExecResult be RegExpBuiltinExec(urlPattern's protocol
10613
  // component's regular expression, protocol).
10614
246k
  auto protocol_exec_result = protocol_component.fast_match(protocol);
10615
246k
  if (!protocol_exec_result) {
10616
97.4k
    return std::nullopt;
10617
97.4k
  }
10618
10619
  // Let usernameExecResult be RegExpBuiltinExec(urlPattern's username
10620
  // component's regular expression, username).
10621
148k
  auto username_exec_result = username_component.fast_match(username);
10622
148k
  if (!username_exec_result) {
10623
1.04k
    return std::nullopt;
10624
1.04k
  }
10625
10626
  // Let passwordExecResult be RegExpBuiltinExec(urlPattern's password
10627
  // component's regular expression, password).
10628
147k
  auto password_exec_result = password_component.fast_match(password);
10629
147k
  if (!password_exec_result) {
10630
393
    return std::nullopt;
10631
393
  }
10632
10633
  // Let hostnameExecResult be RegExpBuiltinExec(urlPattern's hostname
10634
  // component's regular expression, hostname).
10635
147k
  auto hostname_exec_result = hostname_component.fast_match(hostname);
10636
147k
  if (!hostname_exec_result) {
10637
67.9k
    return std::nullopt;
10638
67.9k
  }
10639
10640
  // Let portExecResult be RegExpBuiltinExec(urlPattern's port component's
10641
  // regular expression, port).
10642
79.2k
  auto port_exec_result = port_component.fast_match(port);
10643
79.2k
  if (!port_exec_result) {
10644
280
    return std::nullopt;
10645
280
  }
10646
10647
  // Let pathnameExecResult be RegExpBuiltinExec(urlPattern's pathname
10648
  // component's regular expression, pathname).
10649
78.9k
  auto pathname_exec_result = pathname_component.fast_match(pathname);
10650
78.9k
  if (!pathname_exec_result) {
10651
23.0k
    return std::nullopt;
10652
23.0k
  }
10653
10654
  // Let searchExecResult be RegExpBuiltinExec(urlPattern's search component's
10655
  // regular expression, search).
10656
55.9k
  auto search_exec_result = search_component.fast_match(search);
10657
55.9k
  if (!search_exec_result) {
10658
161
    return std::nullopt;
10659
161
  }
10660
10661
  // Let hashExecResult be RegExpBuiltinExec(urlPattern's hash component's
10662
  // regular expression, hash).
10663
55.7k
  auto hash_exec_result = hash_component.fast_match(hash);
10664
55.7k
  if (!hash_exec_result) {
10665
127
    return std::nullopt;
10666
127
  }
10667
10668
  // Let result be a new URLPatternResult.
10669
55.6k
  auto result = url_pattern_result{};
10670
  // Set result["inputs"] to inputs.
10671
55.6k
  result.inputs = std::move(inputs);
10672
  // Set result["protocol"] to the result of creating a component match result
10673
  // given urlPattern's protocol component, protocol, and protocolExecResult.
10674
55.6k
  result.protocol = protocol_component.create_component_match_result(
10675
55.6k
      std::move(protocol), std::move(*protocol_exec_result));
10676
10677
  // Set result["username"] to the result of creating a component match result
10678
  // given urlPattern's username component, username, and usernameExecResult.
10679
55.6k
  result.username = username_component.create_component_match_result(
10680
55.6k
      std::move(username), std::move(*username_exec_result));
10681
10682
  // Set result["password"] to the result of creating a component match result
10683
  // given urlPattern's password component, password, and passwordExecResult.
10684
55.6k
  result.password = password_component.create_component_match_result(
10685
55.6k
      std::move(password), std::move(*password_exec_result));
10686
10687
  // Set result["hostname"] to the result of creating a component match result
10688
  // given urlPattern's hostname component, hostname, and hostnameExecResult.
10689
55.6k
  result.hostname = hostname_component.create_component_match_result(
10690
55.6k
      std::move(hostname), std::move(*hostname_exec_result));
10691
10692
  // Set result["port"] to the result of creating a component match result given
10693
  // urlPattern's port component, port, and portExecResult.
10694
55.6k
  result.port = port_component.create_component_match_result(
10695
55.6k
      std::move(port), std::move(*port_exec_result));
10696
10697
  // Set result["pathname"] to the result of creating a component match result
10698
  // given urlPattern's pathname component, pathname, and pathnameExecResult.
10699
55.6k
  result.pathname = pathname_component.create_component_match_result(
10700
55.6k
      std::move(pathname), std::move(*pathname_exec_result));
10701
10702
  // Set result["search"] to the result of creating a component match result
10703
  // given urlPattern's search component, search, and searchExecResult.
10704
55.6k
  result.search = search_component.create_component_match_result(
10705
55.6k
      std::move(search), std::move(*search_exec_result));
10706
10707
  // Set result["hash"] to the result of creating a component match result given
10708
  // urlPattern's hash component, hash, and hashExecResult.
10709
55.6k
  result.hash = hash_component.create_component_match_result(
10710
55.6k
      std::move(hash), std::move(*hash_exec_result));
10711
10712
55.6k
  return result;
10713
55.7k
}
10714
10715
}  // namespace ada
10716
#endif  // ADA_INCLUDE_URL_PATTERN
10717
#endif
10718
/* end file include/ada/url_pattern-inl.h */
10719
/* begin file include/ada/url_pattern_helpers-inl.h */
10720
/**
10721
 * @file url_pattern_helpers-inl.h
10722
 * @brief Declaration for the URLPattern helpers.
10723
 */
10724
#ifndef ADA_URL_PATTERN_HELPERS_INL_H
10725
#define ADA_URL_PATTERN_HELPERS_INL_H
10726
10727
#include <cstddef>
10728
#include <cstdint>
10729
#include <optional>
10730
#include <string>
10731
#include <string_view>
10732
#include <utility>
10733
#include <vector>
10734
10735
10736
#if ADA_INCLUDE_URL_PATTERN
10737
namespace ada::url_pattern_helpers {
10738
#if defined(ADA_TESTING) || defined(ADA_LOGGING)
10739
0
inline std::string to_string(token_type type) {
10740
0
  switch (type) {
10741
0
    case token_type::INVALID_CHAR:
10742
0
      return "INVALID_CHAR";
10743
0
    case token_type::OPEN:
10744
0
      return "OPEN";
10745
0
    case token_type::CLOSE:
10746
0
      return "CLOSE";
10747
0
    case token_type::REGEXP:
10748
0
      return "REGEXP";
10749
0
    case token_type::NAME:
10750
0
      return "NAME";
10751
0
    case token_type::CHAR:
10752
0
      return "CHAR";
10753
0
    case token_type::ESCAPED_CHAR:
10754
0
      return "ESCAPED_CHAR";
10755
0
    case token_type::OTHER_MODIFIER:
10756
0
      return "OTHER_MODIFIER";
10757
0
    case token_type::ASTERISK:
10758
0
      return "ASTERISK";
10759
0
    case token_type::END:
10760
0
      return "END";
10761
0
    default:
10762
0
      ada::unreachable();
10763
0
  }
10764
0
}
10765
#endif  // defined(ADA_TESTING) || defined(ADA_LOGGING)
10766
10767
template <url_pattern_regex::regex_concept regex_provider>
10768
135k
constexpr void constructor_string_parser<regex_provider>::rewind() {
10769
  // Set parser's token index to parser's component start.
10770
135k
  token_index = component_start;
10771
  // Set parser's token increment to 0.
10772
135k
  token_increment = 0;
10773
135k
}
10774
10775
template <url_pattern_regex::regex_concept regex_provider>
10776
1.22M
constexpr bool constructor_string_parser<regex_provider>::is_hash_prefix() {
10777
  // Return the result of running is a non-special pattern char given parser,
10778
  // parser's token index and "#".
10779
1.22M
  return is_non_special_pattern_char(token_index, '#');
10780
1.22M
}
10781
10782
template <url_pattern_regex::regex_concept regex_provider>
10783
1.21M
constexpr bool constructor_string_parser<regex_provider>::is_search_prefix() {
10784
  // If result of running is a non-special pattern char given parser, parser's
10785
  // token index and "?" is true, then return true.
10786
1.21M
  if (is_non_special_pattern_char(token_index, '?')) {
10787
3
    return true;
10788
3
  }
10789
10790
  // If parser's token list[parser's token index]'s value is not "?", then
10791
  // return false.
10792
1.21M
  if (token_list[token_index].value != "?") {
10793
1.21M
    return false;
10794
1.21M
  }
10795
10796
  // If previous index is less than 0, then return true.
10797
762
  if (token_index == 0) return true;
10798
  // Let previous index be parser's token index - 1.
10799
762
  auto previous_index = token_index - 1;
10800
  // Let previous token be the result of running get a safe token given parser
10801
  // and previous index.
10802
762
  auto previous_token = get_safe_token(previous_index);
10803
762
  ADA_ASSERT_TRUE(previous_token);
10804
  // If any of the following are true, then return false:
10805
  // - previous token's type is "name".
10806
  // - previous token's type is "regexp".
10807
  // - previous token's type is "close".
10808
  // - previous token's type is "asterisk".
10809
762
  return !(previous_token->type == token_type::NAME ||
10810
687
           previous_token->type == token_type::REGEXP ||
10811
606
           previous_token->type == token_type::CLOSE ||
10812
402
           previous_token->type == token_type::ASTERISK);
10813
762
}
10814
10815
template <url_pattern_regex::regex_concept regex_provider>
10816
constexpr bool
10817
constructor_string_parser<regex_provider>::is_non_special_pattern_char(
10818
6.44M
    size_t index, uint32_t value) const {
10819
  // Let token be the result of running get a safe token given parser and index.
10820
6.44M
  auto token = get_safe_token(index);
10821
6.44M
  ADA_ASSERT_TRUE(token);
10822
10823
  // If token's value is not value, then return false.
10824
  // TODO: Remove this once we make sure get_safe_token returns a non-empty
10825
  // string.
10826
6.44M
  if (!token->value.empty() &&
10827
6.44M
      static_cast<uint32_t>(token->value[0]) != value) {
10828
6.17M
    return false;
10829
6.17M
  }
10830
10831
  // If any of the following are true:
10832
  // - token's type is "char";
10833
  // - token's type is "escaped-char"; or
10834
  // - token's type is "invalid-char",
10835
  // - then return true.
10836
274k
  return token->type == token_type::CHAR ||
10837
94.5k
         token->type == token_type::ESCAPED_CHAR ||
10838
94.4k
         token->type == token_type::INVALID_CHAR;
10839
6.44M
}
10840
10841
template <url_pattern_regex::regex_concept regex_provider>
10842
constexpr const token*
10843
6.67M
constructor_string_parser<regex_provider>::get_safe_token(size_t index) const {
10844
  // If index is less than parser's token list's size, then return parser's
10845
  // token list[index].
10846
6.67M
  if (index < token_list.size()) [[likely]] {
10847
6.67M
    return &token_list[index];
10848
6.67M
  }
10849
10850
  // Assert: parser's token list's size is greater than or equal to 1.
10851
0
  ADA_ASSERT_TRUE(!token_list.empty());
10852
10853
  // Let token be parser's token list[last index].
10854
  // Assert: token's type is "end".
10855
0
  ADA_ASSERT_TRUE(token_list.back().type == token_type::END);
10856
10857
  // Return token.
10858
0
  return &token_list.back();
10859
6.67M
}
10860
10861
template <url_pattern_regex::regex_concept regex_provider>
10862
constexpr bool constructor_string_parser<regex_provider>::is_group_open()
10863
1.95M
    const {
10864
  // If parser's token list[parser's token index]'s type is "open", then return
10865
  // true.
10866
1.95M
  return token_list[token_index].type == token_type::OPEN;
10867
1.95M
}
10868
10869
template <url_pattern_regex::regex_concept regex_provider>
10870
constexpr bool constructor_string_parser<regex_provider>::is_group_close()
10871
12.2k
    const {
10872
  // If parser's token list[parser's token index]'s type is "close", then return
10873
  // true.
10874
12.2k
  return token_list[token_index].type == token_type::CLOSE;
10875
12.2k
}
10876
10877
template <url_pattern_regex::regex_concept regex_provider>
10878
constexpr bool
10879
45.0k
constructor_string_parser<regex_provider>::next_is_authority_slashes() const {
10880
  // If the result of running is a non-special pattern char given parser,
10881
  // parser's token index + 1, and "/" is false, then return false.
10882
45.0k
  if (!is_non_special_pattern_char(token_index + 1, '/')) {
10883
0
    return false;
10884
0
  }
10885
  // If the result of running is a non-special pattern char given parser,
10886
  // parser's token index + 2, and "/" is false, then return false.
10887
45.0k
  if (!is_non_special_pattern_char(token_index + 2, '/')) {
10888
0
    return false;
10889
0
  }
10890
45.0k
  return true;
10891
45.0k
}
10892
10893
template <url_pattern_regex::regex_concept regex_provider>
10894
constexpr bool constructor_string_parser<regex_provider>::is_protocol_suffix()
10895
676k
    const {
10896
  // Return the result of running is a non-special pattern char given parser,
10897
  // parser's token index, and ":".
10898
676k
  return is_non_special_pattern_char(token_index, ':');
10899
676k
}
10900
10901
template <url_pattern_regex::regex_concept regex_provider>
10902
void constructor_string_parser<regex_provider>::change_state(State new_state,
10903
314k
                                                             size_t skip) {
10904
  // If parser's state is not "init", not "authority", and not "done", then set
10905
  // parser's result[parser's state] to the result of running make a component
10906
  // string given parser.
10907
314k
  if (state != State::INIT && state != State::AUTHORITY &&
10908
179k
      state != State::DONE) {
10909
179k
    auto value = make_component_string();
10910
    // TODO: Simplify this.
10911
179k
    switch (state) {
10912
45.0k
      case State::PROTOCOL: {
10913
45.0k
        result.protocol = value;
10914
45.0k
        break;
10915
0
      }
10916
0
      case State::USERNAME: {
10917
0
        result.username = value;
10918
0
        break;
10919
0
      }
10920
0
      case State::PASSWORD: {
10921
0
        result.password = value;
10922
0
        break;
10923
0
      }
10924
45.0k
      case State::HOSTNAME: {
10925
45.0k
        result.hostname = value;
10926
45.0k
        break;
10927
0
      }
10928
0
      case State::PORT: {
10929
0
        result.port = value;
10930
0
        break;
10931
0
      }
10932
88.6k
      case State::PATHNAME: {
10933
88.6k
        result.pathname = value;
10934
88.6k
        break;
10935
0
      }
10936
204
      case State::SEARCH: {
10937
204
        result.search = value;
10938
204
        break;
10939
0
      }
10940
144
      case State::HASH: {
10941
144
        result.hash = value;
10942
144
        break;
10943
0
      }
10944
0
      default:
10945
0
        ada::unreachable();
10946
179k
    }
10947
179k
  }
10948
10949
  // If parser's state is not "init" and new state is not "done", then:
10950
314k
  if (state != State::INIT && new_state != State::DONE) {
10951
    // If parser's state is "protocol", "authority", "username", or "password";
10952
    // new state is "port", "pathname", "search", or "hash"; and parser's
10953
    // result["hostname"] does not exist, then set parser's result["hostname"]
10954
    // to the empty string.
10955
135k
    if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10956
45.3k
         state == State::USERNAME || state == State::PASSWORD) &&
10957
90.0k
        (new_state == State::PORT || new_state == State::PATHNAME ||
10958
90.0k
         new_state == State::SEARCH || new_state == State::HASH) &&
10959
0
        !result.hostname)
10960
0
      result.hostname = "";
10961
135k
  }
10962
10963
  // If parser's state is "protocol", "authority", "username", "password",
10964
  // "hostname", or "port"; new state is "search" or "hash"; and parser's
10965
  // result["pathname"] does not exist, then:
10966
314k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10967
224k
       state == State::USERNAME || state == State::PASSWORD ||
10968
224k
       state == State::HOSTNAME || state == State::PORT) &&
10969
135k
      (new_state == State::SEARCH || new_state == State::HASH) &&
10970
0
      !result.pathname) {
10971
0
    if (protocol_matches_a_special_scheme_flag) {
10972
0
      result.pathname = "/";
10973
0
    } else {
10974
      // Otherwise, set parser's result["pathname"] to the empty string.
10975
0
      result.pathname = "";
10976
0
    }
10977
0
  }
10978
10979
  // If parser's state is "protocol", "authority", "username", "password",
10980
  // "hostname", "port", or "pathname"; new state is "hash"; and parser's
10981
  // result["search"] does not exist, then set parser's result["search"] to
10982
  // the empty string.
10983
314k
  if ((state == State::PROTOCOL || state == State::AUTHORITY ||
10984
224k
       state == State::USERNAME || state == State::PASSWORD ||
10985
224k
       state == State::HOSTNAME || state == State::PORT ||
10986
179k
       state == State::PATHNAME) &&
10987
223k
      new_state == State::HASH && !result.search) {
10988
108
    result.search = "";
10989
108
  }
10990
10991
  // Set parser's state to new state.
10992
314k
  state = new_state;
10993
  // Increment parser's token index by skip.
10994
314k
  token_index += skip;
10995
  // Set parser's component start to parser's token index.
10996
314k
  component_start = token_index;
10997
  // Set parser's token increment to 0.
10998
314k
  token_increment = 0;
10999
314k
}
11000
11001
template <url_pattern_regex::regex_concept regex_provider>
11002
225k
std::string constructor_string_parser<regex_provider>::make_component_string() {
11003
  // Assert: parser's token index is less than parser's token list's size.
11004
225k
  ADA_ASSERT_TRUE(token_index < token_list.size());
11005
11006
  // Let token be parser's token list[parser's token index].
11007
  // Let end index be token's index.
11008
225k
  const auto end_index = token_list[token_index].index;
11009
  // Let component start token be the result of running get a safe token given
11010
  // parser and parser's component start.
11011
225k
  const auto component_start_token = get_safe_token(component_start);
11012
225k
  ADA_ASSERT_TRUE(component_start_token);
11013
  // Let component start input index be component start token's index.
11014
225k
  const auto component_start_input_index = component_start_token->index;
11015
  // Return the code point substring from component start input index to end
11016
  // index within parser's input.
11017
225k
  return std::string(input.substr(component_start_input_index,
11018
225k
                                  end_index - component_start_input_index));
11019
225k
}
11020
11021
template <url_pattern_regex::regex_concept regex_provider>
11022
constexpr bool
11023
540k
constructor_string_parser<regex_provider>::is_an_identity_terminator() const {
11024
  // Return the result of running is a non-special pattern char given parser,
11025
  // parser's token index, and "@".
11026
540k
  return is_non_special_pattern_char(token_index, '@');
11027
540k
}
11028
11029
template <url_pattern_regex::regex_concept regex_provider>
11030
constexpr bool constructor_string_parser<regex_provider>::is_pathname_start()
11031
1.08M
    const {
11032
  // Return the result of running is a non-special pattern char given parser,
11033
  // parser's token index, and "/".
11034
1.08M
  return is_non_special_pattern_char(token_index, '/');
11035
1.08M
}
11036
11037
template <url_pattern_regex::regex_concept regex_provider>
11038
constexpr bool constructor_string_parser<regex_provider>::is_password_prefix()
11039
0
    const {
11040
  // Return the result of running is a non-special pattern char given parser,
11041
  // parser's token index, and ":".
11042
0
  return is_non_special_pattern_char(token_index, ':');
11043
0
}
11044
11045
template <url_pattern_regex::regex_concept regex_provider>
11046
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_open()
11047
540k
    const {
11048
  // Return the result of running is a non-special pattern char given parser,
11049
  // parser's token index, and "[".
11050
540k
  return is_non_special_pattern_char(token_index, '[');
11051
540k
}
11052
11053
template <url_pattern_regex::regex_concept regex_provider>
11054
constexpr bool constructor_string_parser<regex_provider>::is_an_ipv6_close()
11055
540k
    const {
11056
  // Return the result of running is a non-special pattern char given parser,
11057
  // parser's token index, and "]".
11058
540k
  return is_non_special_pattern_char(token_index, ']');
11059
540k
}
11060
11061
template <url_pattern_regex::regex_concept regex_provider>
11062
constexpr bool constructor_string_parser<regex_provider>::is_port_prefix()
11063
540k
    const {
11064
  // Return the result of running is a non-special pattern char given parser,
11065
  // parser's token index, and ":".
11066
540k
  return is_non_special_pattern_char(token_index, ':');
11067
540k
}
11068
11069
3.38M
constexpr void Tokenizer::get_next_code_point() {
11070
3.38M
  ada_log("Tokenizer::get_next_code_point called with index=", next_index);
11071
3.38M
  ADA_ASSERT_TRUE(next_index < input.size());
11072
  // Decode the next UTF-8 code point. If malformed or truncated, mark it as
11073
  // invalid, return the offending byte as the code point, and advance by one
11074
  // to guarantee forward progress.
11075
3.38M
  invalid_code_point = false;
11076
3.38M
  code_point = 0;
11077
3.38M
  size_t number_bytes = 0;
11078
3.38M
  const size_t initial_index = next_index;
11079
3.38M
  unsigned char first_byte = input[next_index];
11080
11081
3.38M
  if ((first_byte & 0x80) == 0) {
11082
    // 1-byte character (ASCII)
11083
3.38M
    next_index++;
11084
3.38M
    code_point = first_byte;
11085
3.38M
    ada_log("Tokenizer::get_next_code_point returning ASCII code point=",
11086
3.38M
            uint32_t(code_point));
11087
3.38M
    ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11088
3.38M
            " input.size()=", input.size());
11089
3.38M
    return;
11090
3.38M
  }
11091
0
  ada_log("Tokenizer::get_next_code_point read first byte=",
11092
0
          uint32_t(first_byte));
11093
0
  if ((first_byte & 0xE0) == 0xC0) {
11094
0
    code_point = first_byte & 0x1F;
11095
0
    number_bytes = 2;
11096
0
    ada_log("Tokenizer::get_next_code_point two bytes");
11097
0
  } else if ((first_byte & 0xF0) == 0xE0) {
11098
0
    code_point = first_byte & 0x0F;
11099
0
    number_bytes = 3;
11100
0
    ada_log("Tokenizer::get_next_code_point three bytes");
11101
0
  } else if ((first_byte & 0xF8) == 0xF0) {
11102
0
    code_point = first_byte & 0x07;
11103
0
    number_bytes = 4;
11104
0
    ada_log("Tokenizer::get_next_code_point four bytes");
11105
0
  }
11106
11107
  // Invalid leading bytes that still match a multi-byte prefix.
11108
0
  if ((number_bytes == 2 && first_byte < 0xC2) ||
11109
0
      (number_bytes == 4 && first_byte > 0xF4)) {
11110
0
    invalid_code_point = true;
11111
0
    code_point = first_byte;
11112
0
    next_index = initial_index + 1;
11113
0
    return;
11114
0
  }
11115
11116
  // Invalid leading byte (e.g., continuation byte outside a sequence).
11117
0
  if (number_bytes == 0) {
11118
0
    invalid_code_point = true;
11119
0
    code_point = first_byte;
11120
0
    next_index = initial_index + 1;
11121
0
    return;
11122
0
  }
11123
11124
  // Truncated UTF-8 sequence.
11125
0
  if (number_bytes + next_index > input.size()) {
11126
0
    invalid_code_point = true;
11127
0
    code_point = first_byte;
11128
0
    next_index = initial_index + 1;
11129
0
    return;
11130
0
  }
11131
11132
0
  for (size_t i = 1 + next_index; i < number_bytes + next_index; ++i) {
11133
0
    unsigned char byte = input[i];
11134
0
    if ((byte & 0xC0) != 0x80) {
11135
0
      invalid_code_point = true;
11136
0
      code_point = first_byte;
11137
0
      next_index = initial_index + 1;
11138
0
      return;
11139
0
    }
11140
0
    ada_log("Tokenizer::get_next_code_point read byte=", uint32_t(byte));
11141
0
    code_point = (code_point << 6) | (byte & 0x3F);
11142
0
  }
11143
0
  ada_log("Tokenizer::get_next_code_point returning non-ASCII code point=",
11144
0
          uint32_t(code_point));
11145
0
  ada_log("Tokenizer::get_next_code_point next_index =", next_index,
11146
0
          " input.size()=", input.size());
11147
0
  next_index += number_bytes;
11148
0
}
11149
11150
3.37M
constexpr void Tokenizer::seek_and_get_next_code_point(size_t new_index) {
11151
3.37M
  ada_log("Tokenizer::seek_and_get_next_code_point called with new_index=",
11152
3.37M
          new_index);
11153
  // Set tokenizer's next index to index.
11154
3.37M
  next_index = new_index;
11155
  // Run get the next code point given tokenizer.
11156
3.37M
  get_next_code_point();
11157
3.37M
}
11158
11159
inline void Tokenizer::add_token(token_type type, size_t next_position,
11160
4.49M
                                 size_t value_position, size_t value_length) {
11161
4.49M
  ada_log("Tokenizer::add_token called with type=", to_string(type),
11162
4.49M
          " next_position=", next_position, " value_position=", value_position);
11163
4.49M
  ADA_ASSERT_TRUE(next_position >= value_position);
11164
11165
  // Let token be a new token.
11166
  // Set token's type to type.
11167
  // Set token's index to tokenizer's index.
11168
  // Set token's value to the code point substring from value position with
11169
  // length value length within tokenizer's input.
11170
  // Append token to the back of tokenizer's token list.
11171
4.49M
  token_list.emplace_back(type, index,
11172
4.49M
                          input.substr(value_position, value_length));
11173
  // Set tokenizer's index to next position.
11174
4.49M
  index = next_position;
11175
4.49M
}
11176
11177
inline void Tokenizer::add_token_with_default_length(token_type type,
11178
                                                     size_t next_position,
11179
4.48M
                                                     size_t value_position) {
11180
  // Let computed length be next position - value position.
11181
4.48M
  auto computed_length = next_position - value_position;
11182
  // Run add a token given tokenizer, type, next position, value position, and
11183
  // computed length.
11184
4.48M
  add_token(type, next_position, value_position, computed_length);
11185
4.48M
}
11186
11187
3.22M
inline void Tokenizer::add_token_with_defaults(token_type type) {
11188
3.22M
  ada_log("Tokenizer::add_token_with_defaults called with type=",
11189
3.22M
          to_string(type));
11190
  // Run add a token with default length given tokenizer, type, tokenizer's next
11191
  // index, and tokenizer's index.
11192
3.22M
  add_token_with_default_length(type, next_index, index);
11193
3.22M
}
11194
11195
inline ada_warn_unused std::optional<errors>
11196
Tokenizer::process_tokenizing_error(size_t next_position,
11197
51.9k
                                    size_t value_position) {
11198
  // If tokenizer's policy is "strict", then throw a TypeError.
11199
51.9k
  if (policy == token_policy::strict) {
11200
1.50k
    ada_log("process_tokenizing_error failed with next_position=",
11201
1.50k
            next_position, " value_position=", value_position);
11202
1.50k
    return errors::type_error;
11203
1.50k
  }
11204
  // Assert: tokenizer's policy is "lenient".
11205
50.4k
  ADA_ASSERT_TRUE(policy == token_policy::lenient);
11206
  // Run add a token with default length given tokenizer, "invalid-char", next
11207
  // position, and value position.
11208
50.4k
  add_token_with_default_length(token_type::INVALID_CHAR, next_position,
11209
50.4k
                                value_position);
11210
50.4k
  return std::nullopt;
11211
51.9k
}
11212
11213
template <url_pattern_encoding_callback F>
11214
593k
token* url_pattern_parser<F>::try_consume_modifier_token() {
11215
  // Let token be the result of running try to consume a token given parser and
11216
  // "other-modifier".
11217
593k
  auto token = try_consume_token(token_type::OTHER_MODIFIER);
11218
  // If token is not null, then return token.
11219
593k
  if (token) return token;
11220
  // Set token to the result of running try to consume a token given parser and
11221
  // "asterisk".
11222
  // Return token.
11223
592k
  return try_consume_token(token_type::ASTERISK);
11224
593k
}
11225
11226
template <url_pattern_encoding_callback F>
11227
token* url_pattern_parser<F>::try_consume_regexp_or_wildcard_token(
11228
3.25M
    const token* name_token) {
11229
  // Let token be the result of running try to consume a token given parser and
11230
  // "regexp".
11231
3.25M
  auto token = try_consume_token(token_type::REGEXP);
11232
  // If name token is null and token is null, then set token to the result of
11233
  // running try to consume a token given parser and "asterisk".
11234
3.25M
  if (!name_token && !token) {
11235
3.24M
    token = try_consume_token(token_type::ASTERISK);
11236
3.24M
  }
11237
  // Return token.
11238
3.25M
  return token;
11239
3.25M
}
11240
11241
template <url_pattern_encoding_callback F>
11242
17.5M
token* url_pattern_parser<F>::try_consume_token(token_type type) {
11243
17.5M
  ada_log("url_pattern_parser::try_consume_token called with type=",
11244
17.5M
          to_string(type));
11245
  // Assert: parser's index is less than parser's token list size.
11246
17.5M
  ADA_ASSERT_TRUE(index < tokens.size());
11247
  // Let next token be parser's token list[parser's index].
11248
17.5M
  auto& next_token = tokens[index];
11249
  // If next token's type is not type return null.
11250
17.5M
  if (next_token.type != type) return nullptr;
11251
  // Increase parser's index by 1.
11252
3.31M
  index++;
11253
  // Return next token.
11254
3.31M
  return &next_token;
11255
17.5M
}
11256
11257
template <url_pattern_encoding_callback F>
11258
5.89k
std::string url_pattern_parser<F>::consume_text() {
11259
  // Let result be the empty string.
11260
5.89k
  std::string result{};
11261
  // While true:
11262
15.6k
  while (true) {
11263
    // Let token be the result of running try to consume a token given parser
11264
    // and "char".
11265
15.6k
    auto token = try_consume_token(token_type::CHAR);
11266
    // If token is null, then set token to the result of running try to consume
11267
    // a token given parser and "escaped-char".
11268
15.6k
    if (!token) token = try_consume_token(token_type::ESCAPED_CHAR);
11269
    // If token is null, then break.
11270
15.6k
    if (!token) break;
11271
    // Append token's value to the end of result.
11272
9.75k
    result.append(token->value);
11273
9.75k
  }
11274
  // Return result.
11275
5.89k
  return result;
11276
5.89k
}
11277
11278
template <url_pattern_encoding_callback F>
11279
1.10M
bool url_pattern_parser<F>::consume_required_token(token_type type) {
11280
1.10M
  ada_log("url_pattern_parser::consume_required_token called with type=",
11281
1.10M
          to_string(type));
11282
  // Let result be the result of running try to consume a token given parser and
11283
  // type.
11284
1.10M
  return try_consume_token(type) != nullptr;
11285
1.10M
}
11286
11287
template <url_pattern_encoding_callback F>
11288
std::optional<errors>
11289
2.29M
url_pattern_parser<F>::maybe_add_part_from_the_pending_fixed_value() {
11290
  // If parser's pending fixed value is the empty string, then return.
11291
2.29M
  if (pending_fixed_value.empty()) {
11292
2.02M
    ada_log("pending_fixed_value is empty");
11293
2.02M
    return std::nullopt;
11294
2.02M
  }
11295
  // Let encoded value be the result of running parser's encoding callback given
11296
  // parser's pending fixed value.
11297
267k
  auto encoded_value = encoding_callback(pending_fixed_value);
11298
267k
  if (!encoded_value) {
11299
4.33k
    ada_log("failed to encode pending_fixed_value: ", pending_fixed_value);
11300
4.33k
    return encoded_value.error();
11301
4.33k
  }
11302
  // Set parser's pending fixed value to the empty string.
11303
262k
  pending_fixed_value.clear();
11304
  // Let part be a new part whose type is "fixed-text", value is encoded value,
11305
  // and modifier is "none".
11306
  // Append part to parser's part list.
11307
262k
  parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11308
262k
                     std::move(*encoded_value),
11309
262k
                     url_pattern_part_modifier::none);
11310
262k
  return std::nullopt;
11311
267k
}
11312
11313
template <url_pattern_encoding_callback F>
11314
std::optional<errors> url_pattern_parser<F>::add_part(
11315
    std::string_view prefix, token* name_token, token* regexp_or_wildcard_token,
11316
593k
    std::string_view suffix, token* modifier_token) {
11317
  // Let modifier be "none".
11318
593k
  auto modifier = url_pattern_part_modifier::none;
11319
  // If modifier token is not null:
11320
593k
  if (modifier_token) {
11321
    // If modifier token's value is "?" then set modifier to "optional".
11322
5.01k
    if (modifier_token->value == "?") {
11323
571
      modifier = url_pattern_part_modifier::optional;
11324
4.44k
    } else if (modifier_token->value == "*") {
11325
      // Otherwise if modifier token's value is "*" then set modifier to
11326
      // "zero-or-more".
11327
3.62k
      modifier = url_pattern_part_modifier::zero_or_more;
11328
3.62k
    } else if (modifier_token->value == "+") {
11329
      // Otherwise if modifier token's value is "+" then set modifier to
11330
      // "one-or-more".
11331
817
      modifier = url_pattern_part_modifier::one_or_more;
11332
817
    }
11333
5.01k
  }
11334
  // If name token is null and regexp or wildcard token is null and modifier
11335
  // is "none":
11336
593k
  if (!name_token && !regexp_or_wildcard_token &&
11337
1.35k
      modifier == url_pattern_part_modifier::none) {
11338
    // Append prefix to the end of parser's pending fixed value.
11339
904
    pending_fixed_value.append(prefix);
11340
904
    return std::nullopt;
11341
904
  }
11342
  // Run maybe add a part from the pending fixed value given parser.
11343
593k
  if (auto error = maybe_add_part_from_the_pending_fixed_value()) {
11344
49
    return *error;
11345
49
  }
11346
  // If name token is null and regexp or wildcard token is null:
11347
592k
  if (!name_token && !regexp_or_wildcard_token) {
11348
    // Assert: suffix is the empty string.
11349
432
    ADA_ASSERT_TRUE(suffix.empty());
11350
    // If prefix is the empty string, then return.
11351
432
    if (prefix.empty()) return std::nullopt;
11352
    // Let encoded value be the result of running parser's encoding callback
11353
    // given prefix.
11354
366
    auto encoded_value = encoding_callback(prefix);
11355
366
    if (!encoded_value) {
11356
11
      return encoded_value.error();
11357
11
    }
11358
    // Let part be a new part whose type is "fixed-text", value is encoded
11359
    // value, and modifier is modifier.
11360
    // Append part to parser's part list.
11361
355
    parts.emplace_back(url_pattern_part_type::FIXED_TEXT,
11362
355
                       std::move(*encoded_value), modifier);
11363
355
    return std::nullopt;
11364
366
  }
11365
  // Let regexp value be the empty string.
11366
592k
  std::string regexp_value{};
11367
  // If regexp or wildcard token is null, then set regexp value to parser's
11368
  // segment wildcard regexp.
11369
592k
  if (!regexp_or_wildcard_token) {
11370
2.37k
    regexp_value = segment_wildcard_regexp;
11371
590k
  } else if (regexp_or_wildcard_token->type == token_type::ASTERISK) {
11372
    // Otherwise if regexp or wildcard token's type is "asterisk", then set
11373
    // regexp value to the full wildcard regexp value.
11374
587k
    regexp_value = ".*";
11375
587k
  } else {
11376
    // Otherwise set regexp value to regexp or wildcard token's value.
11377
2.38k
    regexp_value = regexp_or_wildcard_token->value;
11378
2.38k
  }
11379
  // Let type be "regexp".
11380
592k
  auto type = url_pattern_part_type::REGEXP;
11381
  // If regexp value is parser's segment wildcard regexp:
11382
592k
  if (regexp_value == segment_wildcard_regexp) {
11383
    // Set type to "segment-wildcard".
11384
2.41k
    type = url_pattern_part_type::SEGMENT_WILDCARD;
11385
    // Set regexp value to the empty string.
11386
2.41k
    regexp_value.clear();
11387
590k
  } else if (regexp_value == ".*") {
11388
    // Otherwise if regexp value is the full wildcard regexp value:
11389
    // Set type to "full-wildcard".
11390
587k
    type = url_pattern_part_type::FULL_WILDCARD;
11391
    // Set regexp value to the empty string.
11392
587k
    regexp_value.clear();
11393
587k
  }
11394
  // Let name be the empty string.
11395
592k
  std::string name{};
11396
  // If name token is not null, then set name to name token's value.
11397
592k
  if (name_token) {
11398
2.41k
    name = name_token->value;
11399
590k
  } else if (regexp_or_wildcard_token != nullptr) {
11400
    // Otherwise if regexp or wildcard token is not null:
11401
    // Set name to parser's next numeric name, serialized.
11402
590k
    name = std::to_string(next_numeric_name);
11403
    // Increment parser's next numeric name by 1.
11404
590k
    next_numeric_name++;
11405
590k
  }
11406
  // If the result of running is a duplicate name given parser and name is
11407
  // true, then throw a TypeError. Tracked in a set so this stays O(1): a
11408
  // linear scan of the part list here is O(n^2) over a pattern string with n
11409
  // named parts (e.g. "/*" repeated), an algorithmic-complexity DoS.
11410
592k
  if (!name.empty() && !seen_names.insert(name).second) {
11411
13
    return errors::type_error;
11412
13
  }
11413
  // Let encoded prefix be the result of running parser's encoding callback
11414
  // given prefix.
11415
592k
  auto encoded_prefix = encoding_callback(prefix);
11416
592k
  if (!encoded_prefix) return encoded_prefix.error();
11417
  // Let encoded suffix be the result of running parser's encoding callback
11418
  // given suffix.
11419
592k
  auto encoded_suffix = encoding_callback(suffix);
11420
592k
  if (!encoded_suffix) return encoded_suffix.error();
11421
  // Let part be a new part whose type is type, value is regexp value,
11422
  // modifier is modifier, name is name, prefix is encoded prefix, and suffix
11423
  // is encoded suffix.
11424
  // Append part to parser's part list.
11425
592k
  parts.emplace_back(type, std::move(regexp_value), modifier, std::move(name),
11426
592k
                     std::move(*encoded_prefix), std::move(*encoded_suffix));
11427
592k
  return std::nullopt;
11428
592k
}
11429
11430
template <url_pattern_encoding_callback F>
11431
tl::expected<std::vector<url_pattern_part>, errors> parse_pattern_string(
11432
    std::string_view input, url_pattern_compile_component_options& options,
11433
1.11M
    F& encoding_callback) {
11434
1.11M
  ada_log("parse_pattern_string input=", input);
11435
  // Let parser be a new pattern parser whose encoding callback is encoding
11436
  // callback and segment wildcard regexp is the result of running generate a
11437
  // segment wildcard regexp given options.
11438
1.11M
  auto parser = url_pattern_parser<F>(
11439
1.11M
      encoding_callback, generate_segment_wildcard_regexp(options));
11440
  // Set parser's token list to the result of running tokenize given input and
11441
  // "strict".
11442
1.11M
  auto tokenize_result = tokenize(input, token_policy::strict);
11443
1.11M
  if (!tokenize_result) {
11444
1.50k
    ada_log("parse_pattern_string tokenize failed");
11445
1.50k
    return tl::unexpected(tokenize_result.error());
11446
1.50k
  }
11447
1.10M
  parser.tokens = std::move(*tokenize_result);
11448
11449
  // While parser's index is less than parser's token list's size:
11450
4.35M
  while (parser.can_continue()) {
11451
    // Let char token be the result of running try to consume a token given
11452
    // parser and "char".
11453
3.24M
    auto char_token = parser.try_consume_token(token_type::CHAR);
11454
    // Let name token be the result of running try to consume a token given
11455
    // parser and "name".
11456
3.24M
    auto name_token = parser.try_consume_token(token_type::NAME);
11457
    // Let regexp or wildcard token be the result of running try to consume a
11458
    // regexp or wildcard token given parser and name token.
11459
3.24M
    auto regexp_or_wildcard_token =
11460
3.24M
        parser.try_consume_regexp_or_wildcard_token(name_token);
11461
    // If name token is not null or regexp or wildcard token is not null:
11462
3.24M
    if (name_token || regexp_or_wildcard_token) {
11463
      // Let prefix be the empty string.
11464
591k
      std::string prefix{};
11465
      // If char token is not null then set prefix to char token's value.
11466
591k
      if (char_token) prefix = char_token->value;
11467
      // If prefix is not the empty string and not options's prefix code point:
11468
591k
      if (!prefix.empty() && prefix != options.get_prefix()) {
11469
        // Append prefix to the end of parser's pending fixed value.
11470
4.86k
        parser.pending_fixed_value.append(prefix);
11471
        // Set prefix to the empty string.
11472
4.86k
        prefix.clear();
11473
4.86k
      }
11474
      // Run maybe add a part from the pending fixed value given parser.
11475
591k
      if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11476
396
        ada_log("maybe_add_part_from_the_pending_fixed_value failed");
11477
396
        return tl::unexpected(*error);
11478
396
      }
11479
      // Let modifier token be the result of running try to consume a modifier
11480
      // token given parser.
11481
591k
      auto modifier_token = parser.try_consume_modifier_token();
11482
      // Run add a part given parser, prefix, name token, regexp or wildcard
11483
      // token, the empty string, and modifier token.
11484
591k
      if (auto error =
11485
591k
              parser.add_part(prefix, name_token, regexp_or_wildcard_token, "",
11486
591k
                              modifier_token)) {
11487
13
        ada_log("parser.add_part failed");
11488
13
        return tl::unexpected(*error);
11489
13
      }
11490
      // Continue.
11491
591k
      continue;
11492
591k
    }
11493
11494
    // Let fixed token be char token.
11495
2.65M
    auto fixed_token = char_token;
11496
    // If fixed token is null, then set fixed token to the result of running try
11497
    // to consume a token given parser and "escaped-char".
11498
2.65M
    if (!fixed_token)
11499
1.11M
      fixed_token = parser.try_consume_token(token_type::ESCAPED_CHAR);
11500
    // If fixed token is not null:
11501
2.65M
    if (fixed_token) {
11502
      // Append fixed token's value to parser's pending fixed value.
11503
1.54M
      parser.pending_fixed_value.append(fixed_token->value);
11504
      // Continue.
11505
1.54M
      continue;
11506
1.54M
    }
11507
    // Let open token be the result of running try to consume a token given
11508
    // parser and "open".
11509
1.11M
    auto open_token = parser.try_consume_token(token_type::OPEN);
11510
    // If open token is not null:
11511
1.11M
    if (open_token) {
11512
      // Set prefix be the result of running consume text given parser.
11513
2.94k
      auto prefix_ = parser.consume_text();
11514
      // Set name token to the result of running try to consume a token given
11515
      // parser and "name".
11516
2.94k
      name_token = parser.try_consume_token(token_type::NAME);
11517
      // Set regexp or wildcard token to the result of running try to consume a
11518
      // regexp or wildcard token given parser and name token.
11519
2.94k
      regexp_or_wildcard_token =
11520
2.94k
          parser.try_consume_regexp_or_wildcard_token(name_token);
11521
      // Let suffix be the result of running consume text given parser.
11522
2.94k
      auto suffix_ = parser.consume_text();
11523
      // Run consume a required token given parser and "close".
11524
2.94k
      if (!parser.consume_required_token(token_type::CLOSE)) {
11525
117
        ada_log("parser.consume_required_token failed");
11526
117
        return tl::unexpected(errors::type_error);
11527
117
      }
11528
      // Set modifier token to the result of running try to consume a modifier
11529
      // token given parser.
11530
2.82k
      auto modifier_token = parser.try_consume_modifier_token();
11531
      // Run add a part given parser, prefix, name token, regexp or wildcard
11532
      // token, suffix, and modifier token.
11533
2.82k
      if (auto error =
11534
2.82k
              parser.add_part(prefix_, name_token, regexp_or_wildcard_token,
11535
2.82k
                              suffix_, modifier_token)) {
11536
86
        return tl::unexpected(*error);
11537
86
      }
11538
      // Continue.
11539
2.74k
      continue;
11540
2.82k
    }
11541
    // Run maybe add a part from the pending fixed value given parser.
11542
1.10M
    if (auto error = parser.maybe_add_part_from_the_pending_fixed_value()) {
11543
3.88k
      ada_log("maybe_add_part_from_the_pending_fixed_value failed on line 992");
11544
3.88k
      return tl::unexpected(*error);
11545
3.88k
    }
11546
    // Run consume a required token given parser and "end".
11547
1.10M
    if (!parser.consume_required_token(token_type::END)) {
11548
1.11k
      return tl::unexpected(errors::type_error);
11549
1.11k
    }
11550
1.10M
  }
11551
1.10M
  ada_log("parser.parts size is: ", parser.parts.size());
11552
  // Return parser's part list.
11553
1.10M
  return parser.parts;
11554
1.10M
}
11555
11556
template <url_pattern_regex::regex_concept regex_provider>
11557
bool protocol_component_matches_special_scheme(
11558
176k
    url_pattern_component<regex_provider>& component) {
11559
  // Optimization: Use fast_test for simple patterns to avoid regex overhead
11560
176k
  switch (component.type) {
11561
23.9k
    case url_pattern_component_type::EMPTY:
11562
      // Empty pattern can't match any special scheme
11563
23.9k
      return false;
11564
121k
    case url_pattern_component_type::EXACT_MATCH:
11565
      // Direct string comparison for exact match patterns
11566
121k
      return component.exact_match_value == "http" ||
11567
121k
             component.exact_match_value == "https" ||
11568
254
             component.exact_match_value == "ws" ||
11569
233
             component.exact_match_value == "wss" ||
11570
221
             component.exact_match_value == "ftp" ||
11571
207
             component.exact_match_value == "file";
11572
29.6k
    case url_pattern_component_type::FULL_WILDCARD:
11573
      // Full wildcard matches everything including special schemes
11574
29.6k
      return true;
11575
1.45k
    case url_pattern_component_type::REGEXP:
11576
      // Fall back to regex matching for complex patterns
11577
1.45k
      auto& regex = component.regexp;
11578
1.45k
      return regex_provider::regex_match("http", regex) ||
11579
855
             regex_provider::regex_match("https", regex) ||
11580
805
             regex_provider::regex_match("ws", regex) ||
11581
768
             regex_provider::regex_match("wss", regex) ||
11582
751
             regex_provider::regex_match("ftp", regex) ||
11583
733
             regex_provider::regex_match("file", regex);
11584
176k
  }
11585
0
  ada::unreachable();
11586
0
}
11587
11588
template <url_pattern_regex::regex_concept regex_provider>
11589
inline std::optional<errors> constructor_string_parser<
11590
46.4k
    regex_provider>::compute_protocol_matches_special_scheme_flag() {
11591
46.4k
  ada_log(
11592
46.4k
      "constructor_string_parser::compute_protocol_matches_special_scheme_"
11593
46.4k
      "flag");
11594
  // Let protocol string be the result of running make a component string given
11595
  // parser.
11596
46.4k
  auto protocol_string = make_component_string();
11597
  // Let protocol component be the result of compiling a component given
11598
  // protocol string, canonicalize a protocol, and default options.
11599
46.4k
  auto protocol_component = url_pattern_component<regex_provider>::compile(
11600
46.4k
      protocol_string, canonicalize_protocol,
11601
46.4k
      url_pattern_compile_component_options::DEFAULT);
11602
46.4k
  if (!protocol_component) {
11603
1.38k
    ada_log("url_pattern_component::compile failed for protocol_string ",
11604
1.38k
            protocol_string);
11605
1.38k
    return protocol_component.error();
11606
1.38k
  }
11607
  // If the result of running protocol component matches a special scheme given
11608
  // protocol component is true, then set parser's protocol matches a special
11609
  // scheme flag to true.
11610
45.0k
  if (protocol_component_matches_special_scheme(*protocol_component)) {
11611
45.0k
    protocol_matches_a_special_scheme_flag = true;
11612
45.0k
  }
11613
45.0k
  return std::nullopt;
11614
46.4k
}
11615
11616
template <url_pattern_regex::regex_concept regex_provider>
11617
tl::expected<url_pattern_init, errors>
11618
90.0k
constructor_string_parser<regex_provider>::parse(std::string_view input) {
11619
90.0k
  ada_log("constructor_string_parser::parse input=", input);
11620
  // Let parser be a new constructor string parser whose input is input and
11621
  // token list is the result of running tokenize given input and "lenient".
11622
90.0k
  auto token_list = tokenize(input, token_policy::lenient);
11623
90.0k
  if (!token_list) {
11624
0
    return tl::unexpected(token_list.error());
11625
0
  }
11626
90.0k
  auto parser = constructor_string_parser(input, std::move(*token_list));
11627
11628
  // While parser's token index is less than parser's token list size:
11629
2.09M
  while (parser.token_index < parser.token_list.size()) {
11630
    // Set parser's token increment to 1.
11631
2.09M
    parser.token_increment = 1;
11632
11633
    // If parser's token list[parser's token index]'s type is "end" then:
11634
2.09M
    if (parser.token_list[parser.token_index].type == token_type::END) {
11635
      // If parser's state is "init":
11636
132k
      if (parser.state == State::INIT) {
11637
        // Run rewind given parser.
11638
43.6k
        parser.rewind();
11639
        // If the result of running is a hash prefix given parser is true, then
11640
        // run change state given parser, "hash" and 1.
11641
43.6k
        if (parser.is_hash_prefix()) {
11642
0
          parser.change_state(State::HASH, 1);
11643
43.6k
        } else if (parser.is_search_prefix()) {
11644
          // Otherwise if the result of running is a search prefix given parser
11645
          // is true: Run change state given parser, "search" and 1.
11646
0
          parser.change_state(State::SEARCH, 1);
11647
43.6k
        } else {
11648
          // Run change state given parser, "pathname" and 0.
11649
43.6k
          parser.change_state(State::PATHNAME, 0);
11650
43.6k
        }
11651
        // Increment parser's token index by parser's token increment.
11652
43.6k
        parser.token_index += parser.token_increment;
11653
        // Continue.
11654
43.6k
        continue;
11655
43.6k
      }
11656
11657
88.6k
      if (parser.state == State::AUTHORITY) {
11658
        // If parser's state is "authority":
11659
        // Run rewind and set state given parser, and "hostname".
11660
0
        parser.rewind();
11661
0
        parser.change_state(State::HOSTNAME, 0);
11662
        // Increment parser's token index by parser's token increment.
11663
0
        parser.token_index += parser.token_increment;
11664
        // Continue.
11665
0
        continue;
11666
0
      }
11667
11668
      // Run change state given parser, "done" and 0.
11669
88.6k
      parser.change_state(State::DONE, 0);
11670
      // Break.
11671
88.6k
      break;
11672
88.6k
    }
11673
11674
    // If the result of running is a group open given parser is true:
11675
1.95M
    if (parser.is_group_open()) {
11676
      // Increment parser's group depth by 1.
11677
3.52k
      parser.group_depth += 1;
11678
      // Increment parser's token index by parser's token increment.
11679
3.52k
      parser.token_index += parser.token_increment;
11680
      // Continue.
11681
3.52k
      continue;
11682
3.52k
    }
11683
11684
    // If parser's group depth is greater than 0:
11685
1.95M
    if (parser.group_depth > 0) {
11686
      // If the result of running is a group close given parser is true, then
11687
      // decrement parser's group depth by 1.
11688
12.2k
      if (parser.is_group_close()) {
11689
2.50k
        parser.group_depth -= 1;
11690
9.71k
      } else {
11691
        // Increment parser's token index by parser's token increment.
11692
9.71k
        parser.token_index += parser.token_increment;
11693
9.71k
        continue;
11694
9.71k
      }
11695
12.2k
    }
11696
11697
    // Switch on parser's state and run the associated steps:
11698
1.94M
    switch (parser.state) {
11699
387k
      case State::INIT: {
11700
        // If the result of running is a protocol suffix given parser is true:
11701
387k
        if (parser.is_protocol_suffix()) {
11702
          // Run rewind and set state given parser and "protocol".
11703
46.4k
          parser.rewind();
11704
46.4k
          parser.change_state(State::PROTOCOL, 0);
11705
46.4k
        }
11706
387k
        break;
11707
0
      }
11708
288k
      case State::PROTOCOL: {
11709
        // If the result of running is a protocol suffix given parser is true:
11710
288k
        if (parser.is_protocol_suffix()) {
11711
          // Run compute protocol matches a special scheme flag given parser.
11712
46.4k
          if (const auto error =
11713
46.4k
                  parser.compute_protocol_matches_special_scheme_flag()) {
11714
1.38k
            ada_log("compute_protocol_matches_special_scheme_flag failed");
11715
1.38k
            return tl::unexpected(*error);
11716
1.38k
          }
11717
          // Let next state be "pathname".
11718
45.0k
          auto next_state = State::PATHNAME;
11719
          // Let skip be 1.
11720
45.0k
          auto skip = 1;
11721
          // If the result of running next is authority slashes given parser is
11722
          // true:
11723
45.0k
          if (parser.next_is_authority_slashes()) {
11724
            // Set next state to "authority".
11725
45.0k
            next_state = State::AUTHORITY;
11726
            // Set skip to 3.
11727
45.0k
            skip = 3;
11728
45.0k
          } else if (parser.protocol_matches_a_special_scheme_flag) {
11729
            // Otherwise if parser's protocol matches a special scheme flag is
11730
            // true, then set next state to "authority".
11731
0
            next_state = State::AUTHORITY;
11732
0
          }
11733
11734
          // Run change state given parser, next state, and skip.
11735
45.0k
          parser.change_state(next_state, skip);
11736
45.0k
        }
11737
287k
        break;
11738
288k
      }
11739
540k
      case State::AUTHORITY: {
11740
        // If the result of running is an identity terminator given parser is
11741
        // true, then run rewind and set state given parser and "username".
11742
540k
        if (parser.is_an_identity_terminator()) {
11743
0
          parser.rewind();
11744
0
          parser.change_state(State::USERNAME, 0);
11745
540k
        } else if (parser.is_pathname_start() || parser.is_search_prefix() ||
11746
495k
                   parser.is_hash_prefix()) {
11747
          // Otherwise if any of the following are true:
11748
          // - the result of running is a pathname start given parser;
11749
          // - the result of running is a search prefix given parser; or
11750
          // - the result of running is a hash prefix given parser,
11751
          // then run rewind and set state given parser and "hostname".
11752
45.0k
          parser.rewind();
11753
45.0k
          parser.change_state(State::HOSTNAME, 0);
11754
45.0k
        }
11755
540k
        break;
11756
288k
      }
11757
0
      case State::USERNAME: {
11758
        // If the result of running is a password prefix given parser is true,
11759
        // then run change state given parser, "password", and 1.
11760
0
        if (parser.is_password_prefix()) {
11761
0
          parser.change_state(State::PASSWORD, 1);
11762
0
        } else if (parser.is_an_identity_terminator()) {
11763
          // Otherwise if the result of running is an identity terminator given
11764
          // parser is true, then run change state given parser, "hostname",
11765
          // and 1.
11766
0
          parser.change_state(State::HOSTNAME, 1);
11767
0
        }
11768
0
        break;
11769
288k
      }
11770
0
      case State::PASSWORD: {
11771
        // If the result of running is an identity terminator given parser is
11772
        // true, then run change state given parser, "hostname", and 1.
11773
0
        if (parser.is_an_identity_terminator()) {
11774
0
          parser.change_state(State::HOSTNAME, 1);
11775
0
        }
11776
0
        break;
11777
288k
      }
11778
540k
      case State::HOSTNAME: {
11779
        // If the result of running is an IPv6 open given parser is true, then
11780
        // increment parser's hostname IPv6 bracket depth by 1.
11781
540k
        if (parser.is_an_ipv6_open()) {
11782
0
          parser.hostname_ipv6_bracket_depth += 1;
11783
540k
        } else if (parser.is_an_ipv6_close()) {
11784
          // Otherwise if the result of running is an IPv6 close given parser is
11785
          // true, then decrement parser's hostname IPv6 bracket depth by 1.
11786
0
          parser.hostname_ipv6_bracket_depth -= 1;
11787
540k
        } else if (parser.is_port_prefix() &&
11788
0
                   parser.hostname_ipv6_bracket_depth == 0) {
11789
          // Otherwise if the result of running is a port prefix given parser is
11790
          // true and parser's hostname IPv6 bracket depth is zero, then run
11791
          // change state given parser, "port", and 1.
11792
0
          parser.change_state(State::PORT, 1);
11793
540k
        } else if (parser.is_pathname_start()) {
11794
          // Otherwise if the result of running is a pathname start given parser
11795
          // is true, then run change state given parser, "pathname", and 0.
11796
45.0k
          parser.change_state(State::PATHNAME, 0);
11797
495k
        } else if (parser.is_search_prefix()) {
11798
          // Otherwise if the result of running is a search prefix given parser
11799
          // is true, then run change state given parser, "search", and 1.
11800
0
          parser.change_state(State::SEARCH, 1);
11801
495k
        } else if (parser.is_hash_prefix()) {
11802
          // Otherwise if the result of running is a hash prefix given parser is
11803
          // true, then run change state given parser, "hash", and 1.
11804
0
          parser.change_state(State::HASH, 1);
11805
0
        }
11806
11807
540k
        break;
11808
288k
      }
11809
0
      case State::PORT: {
11810
        // If the result of running is a pathname start given parser is true,
11811
        // then run change state given parser, "pathname", and 0.
11812
0
        if (parser.is_pathname_start()) {
11813
0
          parser.change_state(State::PATHNAME, 0);
11814
0
        } else if (parser.is_search_prefix()) {
11815
          // Otherwise if the result of running is a search prefix given parser
11816
          // is true, then run change state given parser, "search", and 1.
11817
0
          parser.change_state(State::SEARCH, 1);
11818
0
        } else if (parser.is_hash_prefix()) {
11819
          // Otherwise if the result of running is a hash prefix given parser is
11820
          // true, then run change state given parser, "hash", and 1.
11821
0
          parser.change_state(State::HASH, 1);
11822
0
        }
11823
0
        break;
11824
288k
      }
11825
185k
      case State::PATHNAME: {
11826
        // If the result of running is a search prefix given parser is true,
11827
        // then run change state given parser, "search", and 1.
11828
185k
        if (parser.is_search_prefix()) {
11829
204
          parser.change_state(State::SEARCH, 1);
11830
184k
        } else if (parser.is_hash_prefix()) {
11831
          // Otherwise if the result of running is a hash prefix given parser is
11832
          // true, then run change state given parser, "hash", and 1.
11833
108
          parser.change_state(State::HASH, 1);
11834
108
        }
11835
185k
        break;
11836
288k
      }
11837
2.64k
      case State::SEARCH: {
11838
        // If the result of running is a hash prefix given parser is true, then
11839
        // run change state given parser, "hash", and 1.
11840
2.64k
        if (parser.is_hash_prefix()) {
11841
36
          parser.change_state(State::HASH, 1);
11842
36
        }
11843
2.64k
        break;
11844
288k
      }
11845
1.22k
      case State::HASH: {
11846
        // Do nothing
11847
1.22k
        break;
11848
288k
      }
11849
0
      default: {
11850
        // Assert: This step is never reached.
11851
0
        unreachable();
11852
0
      }
11853
1.94M
    }
11854
11855
    // Increment parser's token index by parser's token increment.
11856
1.94M
    parser.token_index += parser.token_increment;
11857
1.94M
  }
11858
11859
  // If parser's result contains "hostname" and not "port", then set parser's
11860
  // result["port"] to the empty string.
11861
88.6k
  if (parser.result.hostname && !parser.result.port) {
11862
45.0k
    parser.result.port = "";
11863
45.0k
  }
11864
11865
  // Return parser's result.
11866
88.6k
  return parser.result;
11867
90.0k
}
11868
11869
}  // namespace ada::url_pattern_helpers
11870
#endif  // ADA_INCLUDE_URL_PATTERN
11871
#endif
11872
/* end file include/ada/url_pattern_helpers-inl.h */
11873
/* begin file include/ada/url_pattern_list.h */
11874
/**
11875
 * @file url_pattern_list.h
11876
 * @brief Compiled set-level matching over URLPattern pathname patterns.
11877
 *
11878
 * `ada::url_pattern_list` answers the question "which of my N routes matches
11879
 * this pathname, and what are the parameter values?" in a single walk
11880
 * instead of a loop of N `url_pattern::exec` calls. The whole route set is
11881
 * compiled once, by ada::parse_url_pattern_list, into a segment trie with
11882
 * per-node dispatch (direct compares for small fanouts, a first-byte index
11883
 * at the root, perfect-hash projection for wide nodes). Matching a pathname
11884
 * against the compiled set is allocation-free and does not execute any
11885
 * regular expression for routes written in the common static / ":param" /
11886
 * "*" subset; other routes are matched through the regex provider, exactly
11887
 * as ada::url_pattern does.
11888
 *
11889
 * This implements the route-set ("URLPatternList") use case discussed in
11890
 * https://github.com/whatwg/urlpattern/issues/166 for the pathname component.
11891
 *
11892
 * Correctness does not depend on the fast path: inputs or routes that exceed
11893
 * the fast-path limits (documented on the constants in
11894
 * `ada::url_pattern_list_limits`) are matched by a sequential fallback with
11895
 * identical priority semantics, never rejected and never answered wrongly.
11896
 *
11897
 * @see https://urlpattern.spec.whatwg.org/
11898
 */
11899
#ifndef ADA_URL_PATTERN_LIST_H
11900
#define ADA_URL_PATTERN_LIST_H
11901
11902
11903
#include <array>
11904
#include <cstddef>
11905
#include <cstdint>
11906
#include <optional>
11907
#include <span>
11908
#include <string>
11909
#include <string_view>
11910
#include <vector>
11911
11912
#if ADA_INCLUDE_URL_PATTERN
11913
namespace ada {
11914
11915
/**
11916
 * The limits of the url_pattern_list fast path. None of them changes what a
11917
 * list matches: routes and inputs beyond a limit are matched by a sequential
11918
 * fallback with identical semantics.
11919
 * @namespace ada::url_pattern_list_limits
11920
 */
11921
namespace url_pattern_list_limits {
11922
11923
/**
11924
 * Maximum number of captures (":param" groups plus one trailing "*" group)
11925
 * a route may declare and still be compiled into the fast path. Routes with
11926
 * more groups are matched by the sequential fallback; their match is still
11927
 * correct, but only the first `max_captures_per_route` captures are reported
11928
 * (see url_pattern_list_match_result::captures_truncated).
11929
 */
11930
inline constexpr uint32_t max_captures_per_route = 8;
11931
11932
/**
11933
 * Maximum number of '/'-separated segments an input pathname may have and
11934
 * still be matched by the fast path. Longer inputs fall back to the
11935
 * sequential matcher and are still matched correctly.
11936
 */
11937
inline constexpr uint32_t max_fast_path_segments = 24;
11938
11939
/**
11940
 * Maximum input pathname length (bytes) accepted by the fast path; segment
11941
 * offsets are tracked in 16-bit integers internally. Longer inputs fall back
11942
 * to the sequential matcher and are still matched correctly.
11943
 */
11944
inline constexpr uint32_t max_fast_path_pathname_length = 4096;
11945
11946
/**
11947
 * Maximum number of pattern segments a route may have and still be compiled
11948
 * into the trie fast path. Deeper routes are matched by the sequential
11949
 * fallback with identical semantics.
11950
 */
11951
inline constexpr uint32_t max_trie_pattern_segments = 16;
11952
11953
}  // namespace url_pattern_list_limits
11954
11955
/**
11956
 * The result of url_pattern_list::match. A winning route reports its group
11957
 * values in one of two forms, mirroring url_pattern_list::group_names():
11958
 *
11959
 * - Routes in the static / ":param" / "*" subset report zero-allocation
11960
 *   captures: (offset, length) slices of the pathname passed to match, in
11961
 *   capture order (":param" groups left to right, then the "*" group).
11962
 *   `regexp_route` is false and `regexp_groups` is empty.
11963
 * - Routes matched through the regex provider (patterns with custom regexp
11964
 *   groups or "?" / "+" / "*" modifiers) report the group values the
11965
 *   provider's regex_search returned, exactly as url_pattern::exec would for
11966
 *   the pathname component: `regexp_route` is true, `regexp_groups[i]` is the
11967
 *   value of group_names(route_index)[i] (nullopt for a group that did not
11968
 *   participate), and capture_count is 0.
11969
 *
11970
 * Routes with more than url_pattern_list_limits::max_captures_per_route
11971
 * groups report only the first max_captures_per_route slices and set
11972
 * captures_truncated.
11973
 */
11974
struct url_pattern_list_match_result {
11975
  /** One capture: an (offset, length) slice of the matched pathname. */
11976
  struct capture {
11977
    uint32_t offset = 0;
11978
    uint32_t length = 0;
11979
  };
11980
  /** Index of the winning route in the creation order, or -1 for no match. */
11981
  int32_t route_index = -1;
11982
  /** Number of valid entries in captures. */
11983
  uint32_t capture_count = 0;
11984
  /** True when the route has more groups than could be reported as slices. */
11985
  bool captures_truncated = false;
11986
  /** True when the winning route was matched through the regex provider; its
11987
   * group values are in regexp_groups. */
11988
  bool regexp_route = false;
11989
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
11990
      captures{};
11991
  /** Group values of a regexp route, as returned by the provider. */
11992
  std::vector<std::optional<std::string>> regexp_groups{};
11993
11994
0
  [[nodiscard]] bool has_match() const noexcept { return route_index >= 0; }
11995
};
11996
11997
/**
11998
 * @private
11999
 * The compiled tables url_pattern_list::match walks, and the inline matcher
12000
 * over them (url_pattern_list-inl.h). Nothing here is part of the supported
12001
 * API. The route-set compiler that produces these tables is not part of the
12002
 * public headers at all (src/url_pattern_list_compiler.h).
12003
 * @namespace ada::url_pattern_list_detail
12004
 */
12005
namespace url_pattern_list_detail {
12006
12007
/** @private One trie node. Nodes with a projection dispatch keep their hash
12008
 * payload in a separate hash_record, so the common 0/1/direct nodes stay at
12009
 * 24 bytes. */
12010
struct node_record {
12011
  // First static edge (edges are laid out per node, contiguously).
12012
  int32_t first_child = 0;
12013
  // Child node index, -2 - route (pure-leaf shortcut), or -1.
12014
  int32_t param_child = -1;
12015
  // Route index of a "*" ending at this prefix, or -1.
12016
  int32_t wild_route = -1;
12017
  // Route ending exactly here, or -1.
12018
  int32_t terminal_route = -1;
12019
  uint16_t n_static = 0;
12020
  // 0 none, 1 direct compares, 2 projection, 3 linear scan, 4 first-byte
12021
  // index (root only).
12022
  uint8_t dispatch = 0;
12023
  // 1 if the node still has a param/wildcard alternative to backtrack to.
12024
  uint8_t has_alternative = 0;
12025
  // Index of the node's hash_record (dispatch == 2 only).
12026
  uint32_t hash_index = 0;
12027
};
12028
12029
/** @private Projection-dispatch payload of a hashed node. */
12030
struct hash_record {
12031
  uint64_t multiplier = 0;
12032
  uint32_t slot_base = 0;
12033
  // Witness plan: 3 nibble ids | count << 12 | use_len << 14.
12034
  uint16_t witness_plan = 0;
12035
  // Slot table size == 1 << slot_bits.
12036
  uint8_t slot_bits = 0;
12037
  uint8_t reserved = 0;
12038
};
12039
12040
/** @private One static edge; keys <= 16 bytes verify from the packed
12041
 * windows alone. */
12042
struct edge_record {
12043
  // Key bytes [0, min(8, len)), little-endian, zero-padded.
12044
  uint64_t prefix = 0;
12045
  // Key bytes [max(0, len - 8), len), little-endian, zero-padded.
12046
  uint64_t suffix = 0;
12047
  // Child node index; negative values encode -2 - route pure-leaf shortcuts.
12048
  int32_t node = -1;
12049
  // Full key in the blob (read only when key_length > 16).
12050
  uint32_t key_offset = 0;
12051
  uint16_t key_length = 0;
12052
};
12053
12054
/** @private How one route of the set is matched. */
12055
enum class route_mode : uint8_t {
12056
  // Compiled into the trie fast path.
12057
  trie = 0,
12058
  // Safe static/":param"/"*" syntax beyond a fast-path build limit: matched
12059
  // by the sequential segment matcher.
12060
  sequential = 1,
12061
  // Needs URLPattern regexp semantics: matched through the regex provider.
12062
  regexp = 2,
12063
};
12064
12065
/** @private Everything the matcher knows about one route after a hit. */
12066
struct route_record {
12067
  // Packed per-segment kinds, two bits per segment, most significant first;
12068
  // (kind_sequence, kind_length, route index) compared as a tuple is the
12069
  // specificity order.
12070
  uint64_t kind_sequence = 0;
12071
  // Sequential-mode routes: their segments in the segment table.
12072
  uint32_t segment_first = 0;
12073
  uint32_t segment_count = 0;
12074
  // Auxiliary routes that can outrank this route when it wins the fast
12075
  // path: a range of the aux table (usually empty).
12076
  uint32_t challenger_first = 0;
12077
  uint32_t challenger_count = 0;
12078
  // Ordinal among the regexp-mode routes, or -1.
12079
  int32_t regexp_component = -1;
12080
  uint8_t kind_length = 0;
12081
  route_mode mode = route_mode::sequential;
12082
  // Trie-mode routes: capture positions.
12083
  uint8_t n_params = 0;
12084
  uint8_t wild = 0;
12085
  std::array<uint8_t, url_pattern_list_limits::max_captures_per_route>
12086
      param_positions{};
12087
};
12088
12089
/** @private One pattern segment of a sequential-mode route. */
12090
struct segment_record {
12091
  uint32_t text_offset = 0;
12092
  uint32_t text_length = 0;
12093
  // 0 literal, 1 ":param", 2 "*".
12094
  uint8_t kind = 0;
12095
};
12096
12097
/**
12098
 * @private
12099
 * The compiled route set: one arena holding every table, addressed by
12100
 * section offsets, plus the per-route group names. Sections are 8-byte
12101
 * aligned; the blob is zero-padded by 8 bytes so short key windows can be
12102
 * loaded whole.
12103
 */
12104
struct compiled_routes {
12105
  std::vector<uint8_t> arena{};
12106
  std::vector<std::vector<std::string>> group_names{};
12107
  uint32_t nodes_offset = 0;
12108
  uint32_t hashes_offset = 0;
12109
  uint32_t edges_offset = 0;
12110
  uint32_t slots_offset = 0;
12111
  uint32_t blob_offset = 0;
12112
  uint32_t routes_offset = 0;
12113
  uint32_t segments_offset = 0;
12114
  uint32_t aux_offset = 0;
12115
  uint32_t root_index_offset = 0;
12116
  uint32_t n_routes = 0;
12117
  // All auxiliary routes (regexp mode and sequential mode) in insertion
12118
  // order: the aux table's first n_aux_all entries; challenger ranges
12119
  // follow.
12120
  uint32_t n_aux_all = 0;
12121
  uint8_t ignore_case = 0;
12122
12123
  template <typename T>
12124
0
  [[nodiscard]] const T* section(uint32_t offset) const noexcept {
12125
0
    return reinterpret_cast<const T*>(arena.data() + offset);
12126
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
12127
};
12128
12129
/** @private Result of the fast-path engine or of the sequential matcher. */
12130
struct engine_result {
12131
  using capture = url_pattern_list_match_result::capture;
12132
  int32_t route = -1;
12133
  uint32_t capture_count = 0;
12134
  // False when the input exceeded a fast-path limit and the caller must run
12135
  // the sequential fallback over the whole route set.
12136
  bool within_fast_path = true;
12137
  bool captures_truncated = false;
12138
  std::array<capture, url_pattern_list_limits::max_captures_per_route>
12139
      captures{};
12140
};
12141
12142
/**
12143
 * @private
12144
 * Parses and compiles a set of pathname patterns (already base-URL
12145
 * processed). Routes that need the regex provider come back with
12146
 * route_record::regexp_component set to their ordinal in insertion order;
12147
 * the caller compiles those components and fills their group names.
12148
 */
12149
tl::expected<compiled_routes, errors> compile_pathname_patterns(
12150
    std::span<const std::string> patterns, bool ignore_case);
12151
12152
/**
12153
 * @private
12154
 * Sequential reference matcher for one non-regexp route of the compiled set;
12155
 * matches any input (no fast-path limits) with the same semantics as the
12156
 * compiled engine. With `fold_input`, input bytes are ASCII case-folded on
12157
 * the fly (the compiled literals are already folded). On a match, fills
12158
 * `result` (route index is NOT set) and returns true.
12159
 */
12160
bool match_route_sequential(const compiled_routes& tables, uint32_t route,
12161
                            std::string_view pathname, bool fold_input,
12162
                            engine_result& result) noexcept;
12163
12164
/**
12165
 * @private
12166
 * True when the bytes [p, p + n) hold no line terminator. A "*" segment
12167
 * compiles to "(.*)" in the URLPattern regexp, and "." in an ECMAScript
12168
 * regular expression does not match LF or CR, so a wildcard may not capture
12169
 * past one. Deliberately not inlined into the walk (it costs the static and
12170
 * ":param" paths, which never run it), and deliberately defined in
12171
 * url_pattern_list.cpp rather than here: MSVC's ada_never_inline carries no
12172
 * inline linkage, so a definition in this header is emitted in every
12173
 * translation unit.
12174
 */
12175
bool wildcard_tail_ok(const char* p, uint32_t n) noexcept;
12176
12177
/**
12178
 * @private
12179
 * Cheap pre-check for a regexp-mode route before running the provider: the
12180
 * part of the route's segment shape that is certain (see the compiler's
12181
 * approximate_kind_sequence) must fit the pathname -- its anchored literal
12182
 * segments, and its exact segment count when every group is a ":name"
12183
 * segment wildcard. False proves the regular expression cannot match.
12184
 */
12185
bool match_regexp_shape(const compiled_routes& tables, uint32_t route,
12186
                        std::string_view pathname, bool fold_input) noexcept;
12187
12188
/**
12189
 * @private
12190
 * True when route `a` (at insertion index `a_index`) outranks route `b` (at
12191
 * `b_index`): lexicographically smaller kind sequence, insertion order as the
12192
 * tiebreak.
12193
 */
12194
constexpr bool outranks(uint64_t a_sequence, uint32_t a_length, size_t a_index,
12195
                        uint64_t b_sequence, uint32_t b_length,
12196
0
                        size_t b_index) noexcept {
12197
0
  if (a_sequence != b_sequence) {
12198
0
    return a_sequence < b_sequence;
12199
0
  }
12200
0
  if (a_length != b_length) {
12201
0
    return a_length < b_length;
12202
0
  }
12203
0
  return a_index < b_index;
12204
0
}
12205
12206
}  // namespace url_pattern_list_detail
12207
12208
template <url_pattern_regex::regex_concept regex_provider>
12209
class url_pattern_list;
12210
12211
/**
12212
 * @private
12213
 * url_pattern_list befriends these two specializations, so they have to be
12214
 * declared before it: a friend declaration that redeclares the function
12215
 * template instead is not matched by every compiler we support (Apple clang
12216
 * 15 rejects the resulting member access). The documented declarations are
12217
 * in implementation.h, next to parse_url_pattern, and carry the default
12218
 * arguments: this header includes url_pattern.h, which includes
12219
 * implementation.h, so those declarations are always seen first, and a
12220
 * function template may not gain default arguments in a later declaration.
12221
 */
12222
template <url_pattern_regex::regex_concept regex_provider>
12223
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12224
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
12225
                       const std::string_view* base_url,
12226
                       const url_pattern_options* options);
12227
12228
/** @private See above. */
12229
template <url_pattern_regex::regex_concept regex_provider>
12230
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
12231
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns);
12232
12233
/**
12234
 * @brief A compiled set of URLPattern pathname patterns with one-shot
12235
 * matching.
12236
 *
12237
 * Build once with ada::parse_url_pattern_list, then call match() per
12238
 * request. Matching within the fast-path limits is allocation-free and
12239
 * regex-free for routes written in the static / ":param" / "*" subset; other
12240
 * routes are matched through the regex provider (the same regex_search call
12241
 * url_pattern::exec makes) and still participate in the priority order.
12242
 *
12243
 * Match priority is specificity order, not registration order: routes are
12244
 * compared by their per-segment kind sequence (literal < ":param" < "*",
12245
 * lexicographically from the first segment), with insertion order breaking
12246
 * ties. This is the priority scheme of find-my-way (Fastify) and Express-
12247
 * style routers. Whether a standardized URLPatternList should instead use
12248
 * pure first-match-in-insertion-order semantics is an open question; the
12249
 * compiled representation supports either, and the priority rule is
12250
 * deliberately centralized in url_pattern_list_detail::outranks.
12251
 *
12252
 * Scope (v1): the pathname component only. Other URL components are treated
12253
 * as fully wildcarded; match() takes an already-extracted pathname (for
12254
 * example ada::url_aggregator::get_pathname()). Inputs are matched as given
12255
 * and are expected to be in canonical (percent-encoded) form; the pattern
12256
 * side is canonicalized by the URLPattern pattern parser at creation. The
12257
 * subset follows the URLPattern regexp it stands for: a ":param" segment
12258
 * ("[^/]+?") is one non-empty segment, and a "*" tail ("(.*)") matches any
12259
 * bytes except the line terminators LF and CR, which "." never matches.
12260
 *
12261
 * With url_pattern_options::ignore_case, regexp routes are compiled through
12262
 * the provider with the flag set (as url_pattern does), and the static /
12263
 * ":param" / "*" subset compares literal segments with ASCII case folding,
12264
 * which is what a case-insensitive regular expression does over the ASCII
12265
 * text of a canonical pathname.
12266
 *
12267
 * @tparam regex_provider The regex implementation used only for routes that
12268
 *         need URLPattern regexp semantics. Must satisfy
12269
 *         url_pattern_regex::regex_concept.
12270
 */
12271
template <url_pattern_regex::regex_concept regex_provider>
12272
class url_pattern_list {
12273
 public:
12274
  url_pattern_list() = default;
12275
12276
  /**
12277
   * Matches a pathname against the set and returns the winning route with
12278
   * its captures (see url_pattern_list_match_result). The fast path is
12279
   * allocation-free; inputs beyond the fast-path limits are matched by the
12280
   * sequential fallback with identical semantics.
12281
   */
12282
  [[nodiscard]] url_pattern_list_match_result match(
12283
      std::string_view pathname) const;
12284
12285
  /** Number of routes in the set. */
12286
  [[nodiscard]] size_t size() const noexcept { return patterns_.size(); }
12287
12288
  /** The pathname pattern string the route at `route_index` was created
12289
   * from (after base URL processing, if a base URL was given). */
12290
  [[nodiscard]] std::string_view pattern(size_t route_index) const
12291
      ada_lifetime_bound {
12292
    return patterns_[route_index];
12293
  }
12294
12295
  /**
12296
   * Capture group names of the route at `route_index`, in the order of
12297
   * url_pattern_list_match_result::captures / regexp_groups ("*" groups have
12298
   * numeric names, per URLPattern).
12299
   */
12300
  [[nodiscard]] const std::vector<std::string>& group_names(
12301
      size_t route_index) const ada_lifetime_bound {
12302
    return compiled_.group_names[route_index];
12303
  }
12304
12305
  /** The ignore_case option the set was created with. */
12306
  [[nodiscard]] bool ignore_case() const noexcept {
12307
    return compiled_.ignore_case != 0;
12308
  }
12309
12310
  friend tl::expected<url_pattern_list, errors>
12311
  parse_url_pattern_list<regex_provider>(
12312
      std::span<const std::string_view> pathname_patterns,
12313
      const std::string_view* base_url, const url_pattern_options* options);
12314
12315
  friend tl::expected<url_pattern_list, errors> parse_url_pattern_list<
12316
      regex_provider>(std::span<const url_pattern<regex_provider>> patterns);
12317
12318
 private:
12319
  /** @private Compiles the (already processed) pattern strings; the regexp
12320
   * components are then supplied by the caller in insertion order. */
12321
  static tl::expected<url_pattern_list, errors> create(
12322
      std::vector<std::string>&& pathname_patterns, bool ignore_case);
12323
12324
  /** @private Tests one auxiliary route (sequential or regexp mode) and,
12325
   * when it matches and outranks `best`, replaces `best` with it. `probe`
12326
   * is the (possibly case-folded) pathname for the sequential matcher,
12327
   * `pathname` the original for the provider. */
12328
  void consider_route(
12329
      uint32_t route_index, std::string_view pathname, std::string_view probe,
12330
      bool fold_input, url_pattern_list_detail::engine_result& best,
12331
      std::vector<std::optional<std::string>>& best_groups) const;
12332
12333
  /** @private The pattern strings, by route index. */
12334
  std::vector<std::string> patterns_{};
12335
  /** @private The compiled tables. */
12336
  url_pattern_list_detail::compiled_routes compiled_{};
12337
  /** @private Compiled pathname components of the regexp-mode routes, in
12338
   * insertion order. */
12339
  std::vector<url_pattern_component<regex_provider>> regexp_components_{};
12340
};
12341
12342
}  // namespace ada
12343
#endif  // ADA_INCLUDE_URL_PATTERN
12344
#endif  // ADA_URL_PATTERN_LIST_H
12345
/* end file include/ada/url_pattern_list.h */
12346
/* begin file include/ada/url_pattern_list-inl.h */
12347
/**
12348
 * @file url_pattern_list-inl.h
12349
 * @brief The url_pattern_list matcher (inline) and the class template's
12350
 * member definitions.
12351
 *
12352
 * The walk over the compiled tables lives here, as ada_really_inline
12353
 * functions, so that it inlines into url_pattern_list::match; the route-set
12354
 * compiler that builds the tables is in its own translation unit
12355
 * (src/url_pattern_list.cpp) and is not part of the public headers.
12356
 */
12357
#ifndef ADA_URL_PATTERN_LIST_INL_H
12358
#define ADA_URL_PATTERN_LIST_INL_H
12359
12360
12361
#include <bit>
12362
#include <cstdint>
12363
#include <cstring>
12364
#include <optional>
12365
#include <string>
12366
#include <string_view>
12367
#include <utility>
12368
#include <vector>
12369
12370
#if ADA_NEON
12371
#include <arm_neon.h>
12372
#endif
12373
12374
#if ADA_INCLUDE_URL_PATTERN
12375
namespace ada::url_pattern_list_detail {
12376
12377
// ---- byte helpers ----------------------------------------------------------
12378
12379
// Unaligned 8-byte little-endian load; all 8 bytes must be readable. The
12380
// byte-assembly fallback keeps big-endian targets correct (the compiler
12381
// lowers it to a single load plus byte swap).
12382
0
ada_really_inline uint64_t load8_le(const char* p) noexcept {
12383
0
  if constexpr (std::endian::native == std::endian::little) {
12384
0
    uint64_t x;
12385
0
    std::memcpy(&x, p, 8);
12386
0
    return x;
12387
  } else {
12388
    uint64_t x = 0;
12389
    for (uint32_t j = 0; j < 8; j++) {
12390
      x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12391
    }
12392
    return x;
12393
  }
12394
0
}
12395
12396
// Bytes [0, n) of p packed little-endian, zero-padded; reads exactly n
12397
// bytes (n < 8). This is the byte-compare path for short tails.
12398
0
ada_really_inline uint64_t gather_le(const char* p, uint32_t n) noexcept {
12399
0
  uint64_t x = 0;
12400
0
  for (uint32_t j = 0; j < n; j++) {
12401
0
    x |= static_cast<uint64_t>(static_cast<uint8_t>(p[j])) << (8 * j);
12402
0
  }
12403
0
  return x;
12404
0
}
12405
12406
// Mask selecting the low n bytes, 1 <= n <= 7.
12407
0
constexpr uint64_t low_bytes_mask(uint32_t n) noexcept {
12408
0
  return ~0ull >> (64 - 8 * n);
12409
0
}
12410
12411
// ASCII case folding of n bytes from src into dst (the compiler vectorizes
12412
// this loop). Only ASCII letters change: canonical pathnames are ASCII, and
12413
// this is what a case-insensitive regular expression does over them.
12414
0
inline void ascii_fold(const char* src, uint32_t n, char* dst) noexcept {
12415
0
  for (uint32_t i = 0; i < n; i++) {
12416
0
    const uint8_t c = static_cast<uint8_t>(src[i]);
12417
0
    dst[i] = static_cast<char>(c | ((c >= 'A' && c <= 'Z') ? 0x20u : 0u));
12418
0
  }
12419
0
}
12420
12421
// ---- witness machinery -----------------------------------------------------
12422
// Projection dispatch for wide trie nodes. A witness id names a feature of a
12423
// byte string:
12424
//   0..7   the byte at absolute offset id           (0 when out of range)
12425
//   8..15  the byte at offset id-8 counted from the end
12426
// A projection is three witness bytes plus the length packed into one
12427
// uint64; a perfect multiplier turns it into a slot index. Builder and
12428
// matcher call the same accessors and the same packing, so a slot table
12429
// cannot disagree with the gather that reads it.
12430
12431
0
constexpr uint8_t length_byte(uint32_t len) noexcept {
12432
0
  return len < 255u ? static_cast<uint8_t>(len) : static_cast<uint8_t>(255);
12433
0
}
12434
12435
constexpr uint8_t witness_byte(const char* p, uint32_t len,
12436
0
                               uint8_t id) noexcept {  // ids 0..15
12437
0
  uint32_t o = id & 7u;
12438
0
  bool in = o < len;
12439
0
  uint32_t pos = (id & 8u) ? len - 1u - o : o;  // wraps when !in; masked then
12440
0
  pos = in ? pos : 0u;
12441
0
  uint8_t v = len ? static_cast<uint8_t>(p[pos]) : static_cast<uint8_t>(0);
12442
0
  return in ? v : static_cast<uint8_t>(0);
12443
0
}
12444
12445
constexpr uint64_t pack_projection(uint8_t f0, uint8_t f1, uint8_t f2,
12446
0
                                   uint8_t f3) noexcept {
12447
0
  return (static_cast<uint64_t>(f0) << 24) | (static_cast<uint64_t>(f1) << 16) |
12448
0
         (static_cast<uint64_t>(f2) << 8) | static_cast<uint64_t>(f3);
12449
0
}
12450
12451
// Perfect-multiplier slot index; b <= 12 everywhere, so the shift is in
12452
// range and the product needs no modulus.
12453
constexpr uint64_t slot_of(uint64_t proj, uint64_t multiplier,
12454
0
                           uint8_t b) noexcept {
12455
0
  return (proj * multiplier) >> (64 - b);
12456
0
}
12457
12458
// Fixed-shape projection of one string: always 3 witness bytes plus the
12459
// length. Duplicated ids add no information but keep the gather uniform.
12460
constexpr uint64_t project(const char* p, uint32_t len,
12461
0
                           uint16_t plan) noexcept {
12462
0
  return pack_projection(
12463
0
      witness_byte(p, len, static_cast<uint8_t>(plan & 15u)),
12464
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 4) & 15u)),
12465
0
      witness_byte(p, len, static_cast<uint8_t>((plan >> 8) & 15u)),
12466
0
      length_byte(len));
12467
0
}
12468
12469
// ---- segment scan ----------------------------------------------------------
12470
12471
// Splits the pathname into segments in one pass, in place: only segment
12472
// starts are recorded, with a sentinel soff[nseg] = ulen + 1 so that
12473
// seg_len(i) == soff[i + 1] - soff[i] - 1. Returns the segment count, or 0
12474
// when the input has more than max_fast_path_segments segments (the caller
12475
// falls back to the sequential matcher). Requires ulen >= 1 and
12476
// url[0] == '/'.
12477
//
12478
// Two implementations with the same contract: the portable SWAR scan (8
12479
// bytes per step, an exact zero-byte test on x ^ '/'-fill with no false
12480
// positives, bytes >= 0x80 included, and a byte-exact partial load for the
12481
// tail, so the input is never over-read), and on AArch64 a NEON scan for
12482
// inputs of 16 bytes or more (16 bytes per step, the '/' compare narrowed
12483
// to one nibble per byte, the last block overlapping the previous one so
12484
// the input is never over-read). The 1k+ pathname benchmark in the PR
12485
// discussion is what keeps the NEON scan: it is faster at every length and
12486
// 3-4x faster past 1 KB.
12487
struct segment_emitter {
12488
  uint16_t* soff;
12489
  uint32_t nseg = 0;
12490
  uint32_t start = 1;
12491
  // One segment start per set bit of `lanes`; a lane is 1 << shift bits
12492
  // wide (8 for the SWAR masks, 4 for the NEON nibble masks).
12493
  ada_really_inline bool emit(uint64_t lanes, uint32_t base,
12494
0
                              unsigned shift) noexcept {
12495
0
    using url_pattern_list_limits::max_fast_path_segments;
12496
0
    while (lanes) {
12497
0
      const uint32_t pos =
12498
0
          base + (static_cast<uint32_t>(std::countr_zero(lanes)) >> shift);
12499
0
      if (nseg >= max_fast_path_segments) {
12500
0
        return false;
12501
0
      }
12502
0
      soff[nseg++] = static_cast<uint16_t>(start);
12503
0
      start = pos + 1;
12504
0
      lanes &= lanes - 1;
12505
0
    }
12506
0
    return true;
12507
0
  }
12508
0
  ada_really_inline uint32_t finish(uint32_t ulen) noexcept {
12509
0
    using url_pattern_list_limits::max_fast_path_segments;
12510
0
    if (nseg >= max_fast_path_segments) {
12511
0
      return 0;
12512
0
    }
12513
0
    soff[nseg++] = static_cast<uint16_t>(start);
12514
0
    soff[nseg] = static_cast<uint16_t>(ulen + 1);  // sentinel
12515
0
    return nseg;
12516
0
  }
12517
};
12518
12519
ada_really_inline uint32_t scan_segments_swar(const char* url, uint32_t ulen,
12520
0
                                              uint16_t* soff) noexcept {
12521
0
  constexpr uint64_t slashes = 0x2F2F2F2F2F2F2F2Full;
12522
0
  constexpr uint64_t low7 = 0x7F7F7F7F7F7F7F7Full;
12523
0
  // Bit 7 of every byte of x that is zero, exactly: (b & 0x7F) + 0x7F has
12524
0
  // bit 7 set iff the low bits are non-zero, b itself has it set iff b is
12525
0
  // >= 0x80, and no lane can carry into its neighbour.
12526
0
  const auto zero_lanes = [](uint64_t x) noexcept {
12527
0
    return ~(((x & low7) + low7) | x | low7);
12528
0
  };
12529
0
  segment_emitter out{soff};
12530
0
  uint32_t i = 1;
12531
0
  for (; i + 8 <= ulen; i += 8) {
12532
0
    const uint64_t lanes = zero_lanes(load8_le(url + i) ^ slashes);
12533
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12534
0
      return 0;
12535
0
    }
12536
0
  }
12537
0
  if (i < ulen) {  // tail of 1..7 bytes: zero padding is never a '/'
12538
0
    const uint64_t lanes = zero_lanes(gather_le(url + i, ulen - i) ^ slashes);
12539
0
    if (lanes != 0 && !out.emit(lanes, i, 3)) {
12540
0
      return 0;
12541
0
    }
12542
0
  }
12543
0
  return out.finish(ulen);
12544
0
}
12545
12546
#if ADA_NEON
12547
ada_really_inline uint32_t scan_segments_neon(const char* url, uint32_t ulen,
12548
                                              uint16_t* soff) noexcept {
12549
  // Requires ulen >= 16.
12550
  const uint8x16_t slash = vdupq_n_u8('/');
12551
  // One bit per byte lane that holds a '/': compare, narrow each 16-bit
12552
  // pair to its high nibble, keep bit 0 of every nibble.
12553
  const auto slash_lanes = [&](const char* p) noexcept {
12554
    const uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
12555
    const uint64_t nibbles =
12556
        vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(
12557
                          vreinterpretq_u16_u8(vceqq_u8(v, slash)), 4)),
12558
                      0);
12559
    return nibbles & 0x1111111111111111ull;
12560
  };
12561
  segment_emitter out{soff};
12562
  uint32_t i = 0;
12563
  for (; i + 16 <= ulen; i += 16) {
12564
    uint64_t lanes = slash_lanes(url + i);
12565
    if (i == 0) {
12566
      lanes &= ~0xFull;  // the leading '/' is not a separator
12567
    }
12568
    if (lanes != 0 && !out.emit(lanes, i, 2)) {
12569
      return 0;
12570
    }
12571
  }
12572
  if (i < ulen) {  // overlapped last block: drop the lanes already scanned
12573
    const uint32_t off = ulen - 16;
12574
    const uint64_t lanes = slash_lanes(url + off) & (~0ull << ((i - off) * 4));
12575
    if (lanes != 0 && !out.emit(lanes, off, 2)) {
12576
      return 0;
12577
    }
12578
  }
12579
  return out.finish(ulen);
12580
}
12581
#endif  // ADA_NEON
12582
12583
ada_really_inline uint32_t scan_segments(const char* url, uint32_t ulen,
12584
0
                                         uint16_t* soff) noexcept {
12585
0
#if ADA_NEON
12586
0
  if (ulen >= 16) {
12587
0
    return scan_segments_neon(url, ulen, soff);
12588
0
  }
12589
0
#endif
12590
0
  return scan_segments_swar(url, ulen, soff);
12591
0
}
12592
12593
// ---- verify / dispatch -----------------------------------------------------
12594
12595
// One input segment prepared for dispatch: its bytes, its length, and how
12596
// many bytes are readable from p (to the end of the pathname).
12597
struct segment_ref {
12598
  const char* p;
12599
  uint32_t len;
12600
  uint32_t avail;
12601
};
12602
12603
// Segment-vs-edge compare. Keys of 8..16 bytes verify from two whole-word
12604
// loads inside the segment; shorter keys from one masked load when the
12605
// input has 8 readable bytes, else from a byte gather (short tails such as
12606
// "42" or "me"); longer keys through memcmp against the blob. An empty key
12607
// (a pattern such as "/users/") matches exactly the empty segment.
12608
ada_really_inline bool verify_edge(const edge_record& e, const segment_ref& s,
12609
0
                                   const char* blob) noexcept {
12610
0
  const uint32_t len = s.len;
12611
0
  if (len != e.key_length) {
12612
0
    return false;
12613
0
  }
12614
0
  if (len == 0) {
12615
0
    return true;
12616
0
  }
12617
0
  if (len > 16) {
12618
0
    return std::memcmp(s.p, blob + e.key_offset, len) == 0;
12619
0
  }
12620
0
  if (len >= 8) {
12621
0
    return ((load8_le(s.p) ^ e.prefix) |
12622
0
            (load8_le(s.p + len - 8) ^ e.suffix)) == 0;
12623
0
  }
12624
0
  const uint64_t x = s.avail >= 8 ? (load8_le(s.p) & low_bytes_mask(len))
12625
0
                                  : gather_le(s.p, len);
12626
0
  return x == e.prefix;
12627
0
}
12628
12629
// The typed view of the arena the walk reads; derived once per match.
12630
struct table_view {
12631
  const node_record* nodes;
12632
  const hash_record* hashes;
12633
  const edge_record* edges;
12634
  const uint8_t* slots;
12635
  const char* blob;
12636
  const uint16_t* root_index;
12637
};
12638
12639
0
ada_really_inline table_view view_of(const compiled_routes& r) noexcept {
12640
0
  return table_view{r.section<node_record>(r.nodes_offset),
12641
0
                    r.section<hash_record>(r.hashes_offset),
12642
0
                    r.section<edge_record>(r.edges_offset),
12643
0
                    r.section<uint8_t>(r.slots_offset),
12644
0
                    r.section<char>(r.blob_offset),
12645
0
                    r.section<uint16_t>(r.root_index_offset)};
12646
0
}
12647
12648
// Static-child dispatch: returns the child ordinal within the node or -1.
12649
// Correctness never depends on the projection or the index: the candidate
12650
// is always confirmed by a full segment compare, so a bad table can only
12651
// cost time, never change an answer.
12652
ada_really_inline int32_t dispatch_static(const table_view& t,
12653
                                          const node_record& nd,
12654
0
                                          const segment_ref& s) noexcept {
12655
0
  const edge_record* e = t.edges + nd.first_child;
12656
0
  switch (nd.dispatch) {
12657
0
    case 0:
12658
0
      return -1;
12659
0
    case 1: {  // direct: up to 8 compares, each gated by the key length
12660
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12661
0
        if (verify_edge(e[j], s, t.blob)) {
12662
0
          return static_cast<int32_t>(j);
12663
0
        }
12664
0
      }
12665
0
      return -1;
12666
0
    }
12667
0
    case 2: {  // projection: gather -> multiply -> slot -> verify
12668
0
      const hash_record& h = t.hashes[nd.hash_index];
12669
0
      const uint64_t proj = project(s.p, s.len, h.witness_plan);
12670
0
      const uint8_t ord =
12671
0
          t.slots[h.slot_base + slot_of(proj, h.multiplier, h.slot_bits)];
12672
0
      if (ord == 0xFF) {
12673
0
        return -1;
12674
0
      }
12675
0
      return verify_edge(e[ord], s, t.blob) ? static_cast<int32_t>(ord) : -1;
12676
0
    }
12677
0
    case 4: {  // root first-byte index: jump to the run of children that
12678
0
               // share the segment's first byte (children are sorted by it)
12679
0
      if (s.len == 0) {
12680
0
        return -1;  // no first byte; an indexed root has no empty key
12681
0
      }
12682
0
      const uint8_t b = static_cast<uint8_t>(s.p[0]);
12683
0
      uint32_t j = t.root_index[b];
12684
0
      if (j == 0xFFFF) {
12685
0
        return -1;
12686
0
      }
12687
0
      for (; j < nd.n_static && static_cast<uint8_t>(e[j].prefix & 0xFFu) == b;
12688
0
           j++) {
12689
0
        if (verify_edge(e[j], s, t.blob)) {
12690
0
          return static_cast<int32_t>(j);
12691
0
        }
12692
0
      }
12693
0
      return -1;
12694
0
    }
12695
0
    default: {  // linear demotion rung (also carries fanout > 254)
12696
0
      for (uint32_t j = 0; j < nd.n_static; j++) {
12697
0
        if (verify_edge(e[j], s, t.blob)) {
12698
0
          return static_cast<int32_t>(j);
12699
0
        }
12700
0
      }
12701
0
      return -1;
12702
0
    }
12703
0
  }
12704
0
}
12705
12706
// ---- the match residual ----------------------------------------------------
12707
12708
// Matches `pathname` against the compiled tables. When the input is out of
12709
// the fast-path contract, returns with within_fast_path == false and no
12710
// answer; the caller falls back to the sequential matcher.
12711
ada_really_inline void match_compiled(const compiled_routes& r, const char* url,
12712
                                      uint32_t ulen,
12713
0
                                      engine_result& out) noexcept {
12714
0
  using url_pattern_list_limits::max_fast_path_pathname_length;
12715
0
  using url_pattern_list_limits::max_fast_path_segments;
12716
0
  out = engine_result{};
12717
0
  if (ulen == 0 || url[0] != '/' || ulen > max_fast_path_pathname_length) {
12718
0
    out.within_fast_path = false;  // the sequential fallback decides
12719
0
    return;
12720
0
  }
12721
0
  const table_view t = view_of(r);
12722
0
  const route_record* routes = r.section<route_record>(r.routes_offset);
12723
0
12724
0
  // Segment scan; overflow means the input is beyond the fast path.
12725
0
  uint16_t soff[max_fast_path_segments + 1];
12726
0
  const uint32_t nseg = scan_segments(url, ulen, soff);
12727
0
  if (nseg == 0) {
12728
0
    out.within_fast_path = false;
12729
0
    return;
12730
0
  }
12731
0
  const auto seg_len = [&](uint32_t i) {
12732
0
    return static_cast<uint32_t>(soff[i + 1]) - soff[i] - 1u;
12733
0
  };
12734
0
  const auto seg_ref = [&](uint32_t i) {
12735
0
    const uint32_t off = soff[i];
12736
0
    return segment_ref{url + off, seg_len(i), ulen - off};
12737
0
  };
12738
0
12739
0
  // Trie walk with an explicit bounded backtrack stack. Every node offers
12740
0
  // the same three alternatives in the compiled priority order (static
12741
0
  // child, param child, wildcard); a backtrack record stores the resume
12742
0
  // point, and a record is pushed only when the node still has an untried
12743
0
  // alternative, so the stack never exceeds the walk depth. Edges encoded
12744
0
  // as -2 - route are pure-leaf shortcuts.
12745
0
  enum alternative : uint16_t {
12746
0
    alt_static = 0,
12747
0
    alt_param = 1,
12748
0
    alt_wild = 2,
12749
0
    alt_none = 3
12750
0
  };
12751
0
  struct backtrack {
12752
0
    int32_t node;
12753
0
    uint16_t i;
12754
0
    uint16_t resume;
12755
0
  };
12756
0
  backtrack stack[max_fast_path_segments + 1];
12757
0
  uint32_t sp = 0;
12758
0
  int32_t node = 0;
12759
0
  uint32_t i = 0;
12760
0
  uint32_t alt = alt_static;
12761
0
  int32_t route = -1;
12762
0
  uint32_t wild_from = 0;
12763
0
  bool wild = false;
12764
0
  for (;;) {
12765
0
    const node_record& nd = t.nodes[static_cast<size_t>(node)];
12766
0
    if (alt == alt_static) {
12767
0
      if (i == nseg) {
12768
0
        if (nd.terminal_route >= 0) {
12769
0
          route = nd.terminal_route;
12770
0
          break;
12771
0
        }
12772
0
        alt = alt_none;
12773
0
      } else {
12774
0
        const int32_t ord = dispatch_static(t, nd, seg_ref(i));
12775
0
        if (ord >= 0) {
12776
0
          const int32_t nxt = t.edges[static_cast<size_t>(nd.first_child) +
12777
0
                                      static_cast<size_t>(ord)]
12778
0
                                  .node;
12779
0
          if (nxt >= 0) {
12780
0
            if (nd.has_alternative) {
12781
0
              stack[sp++] = {node, static_cast<uint16_t>(i), alt_param};
12782
0
            }
12783
0
            node = nxt;
12784
0
            i++;
12785
0
            continue;
12786
0
          }
12787
0
          // Pure-leaf shortcut.
12788
0
          if (i + 1 == nseg) {
12789
0
            route = -2 - nxt;
12790
0
            break;
12791
0
          }
12792
0
          alt = alt_param;  // dead end: fall through to this node's fallbacks
12793
0
        } else {
12794
0
          alt = alt_param;
12795
0
        }
12796
0
      }
12797
0
    }
12798
0
    if (alt == alt_param) {
12799
0
      const int32_t pc = nd.param_child;
12800
0
      if (pc != -1 && seg_len(i) > 0) {
12801
0
        if (pc >= 0) {
12802
0
          if (nd.wild_route >= 0) {
12803
0
            stack[sp++] = {node, static_cast<uint16_t>(i), alt_wild};
12804
0
          }
12805
0
          node = pc;
12806
0
          i++;
12807
0
          alt = alt_static;
12808
0
          continue;
12809
0
        }
12810
0
        // Pure-leaf param shortcut.
12811
0
        if (i + 1 == nseg) {
12812
0
          route = -2 - pc;
12813
0
          break;
12814
0
        }
12815
0
        alt = alt_wild;  // only the wildcard remains here
12816
0
      } else {
12817
0
        alt = alt_wild;
12818
0
      }
12819
0
    }
12820
0
    if (alt == alt_wild) {
12821
0
      if (nd.wild_route >= 0 &&
12822
0
          wildcard_tail_ok(url + soff[i], ulen - soff[i])) {
12823
0
        route = nd.wild_route;
12824
0
        wild_from = i;
12825
0
        wild = true;
12826
0
        break;
12827
0
      }
12828
0
      // No alternative left at this node: resume from the backtrack stack
12829
0
      // (alt is overwritten by the popped record).
12830
0
    }
12831
0
    if (sp == 0) {
12832
0
      break;  // nothing to resume: miss
12833
0
    }
12834
0
    --sp;
12835
0
    node = stack[sp].node;
12836
0
    i = stack[sp].i;
12837
0
    alt = stack[sp].resume;
12838
0
  }
12839
0
12840
0
  out.route = route;
12841
0
  if (route >= 0) {  // capture extraction: params first, wildcard tail last
12842
0
    const route_record& rm = routes[static_cast<size_t>(route)];
12843
0
    uint32_t nc = 0;
12844
0
    for (uint32_t k = 0; k < rm.n_params; k++) {
12845
0
      const uint32_t p = rm.param_positions[k];
12846
0
      out.captures[nc++] = {soff[p], seg_len(p)};
12847
0
    }
12848
0
    if (wild) {
12849
0
      out.captures[nc++] = {soff[wild_from], ulen - soff[wild_from]};
12850
0
    }
12851
0
    out.capture_count = nc;
12852
0
  }
12853
0
}
12854
12855
}  // namespace ada::url_pattern_list_detail
12856
12857
namespace ada {
12858
12859
template <url_pattern_regex::regex_concept regex_provider>
12860
tl::expected<url_pattern_list<regex_provider>, errors>
12861
url_pattern_list<regex_provider>::create(
12862
    std::vector<std::string>&& pathname_patterns, bool ignore_case) {
12863
  url_pattern_list list{};
12864
  list.patterns_ = std::move(pathname_patterns);
12865
  auto compiled = url_pattern_list_detail::compile_pathname_patterns(
12866
      list.patterns_, ignore_case);
12867
  if (!compiled) {
12868
    return tl::unexpected(compiled.error());
12869
  }
12870
  list.compiled_ = std::move(*compiled);
12871
  return list;
12872
}
12873
12874
template <url_pattern_regex::regex_concept regex_provider>
12875
void url_pattern_list<regex_provider>::consider_route(
12876
    uint32_t route_index, std::string_view pathname, std::string_view probe,
12877
    bool fold_input, url_pattern_list_detail::engine_result& best,
12878
    std::vector<std::optional<std::string>>& best_groups) const {
12879
  namespace detail = url_pattern_list_detail;
12880
  const detail::route_record* routes =
12881
      compiled_.section<detail::route_record>(compiled_.routes_offset);
12882
  const detail::route_record& candidate = routes[route_index];
12883
  // Only test candidates that would outrank the current best.
12884
  if (best.route >= 0) {
12885
    const detail::route_record& current =
12886
        routes[static_cast<size_t>(best.route)];
12887
    if (!detail::outranks(candidate.kind_sequence, candidate.kind_length,
12888
                          route_index, current.kind_sequence,
12889
                          current.kind_length,
12890
                          static_cast<size_t>(best.route))) {
12891
      return;
12892
    }
12893
  }
12894
  if (candidate.mode == detail::route_mode::regexp) {
12895
    // A route whose certain segment shape does not fit the input cannot
12896
    // match at all: skip the provider.
12897
    if (!detail::match_regexp_shape(compiled_, route_index, probe,
12898
                                    fold_input)) {
12899
      return;
12900
    }
12901
    // URLPattern's own test/exec split: regex_match answers yes/no (this is
12902
    // what rejects a non-matching route cheaply; a regex_search miss can
12903
    // cost a scan over every start position), and regex_search then
12904
    // produces the group values exactly as url_pattern::exec does.
12905
    const url_pattern_component<regex_provider>& component =
12906
        regexp_components_[static_cast<size_t>(candidate.regexp_component)];
12907
    if (!component.fast_test(pathname)) {
12908
      return;
12909
    }
12910
    auto groups = component.fast_match(pathname);
12911
    if (groups) {
12912
      best = detail::engine_result{};
12913
      best.route = static_cast<int32_t>(route_index);
12914
      best_groups = std::move(*groups);
12915
    }
12916
  } else {
12917
    detail::engine_result scratch{};
12918
    if (detail::match_route_sequential(compiled_, route_index, probe,
12919
                                       fold_input, scratch)) {
12920
      scratch.route = static_cast<int32_t>(route_index);
12921
      best = scratch;
12922
      best_groups.clear();
12923
    }
12924
  }
12925
}
12926
12927
template <url_pattern_regex::regex_concept regex_provider>
12928
url_pattern_list_match_result url_pattern_list<regex_provider>::match(
12929
    std::string_view pathname) const {
12930
  namespace detail = url_pattern_list_detail;
12931
  using url_pattern_list_limits::max_fast_path_pathname_length;
12932
  // A default-constructed list (or one compiled from no routes) has no
12933
  // tables. Walking them would dereference section pointers into an empty
12934
  // arena.
12935
  if (patterns_.empty() || compiled_.arena.empty()) {
12936
    return {};
12937
  }
12938
  // With ignore_case the compiled literals are ASCII-folded, so the fast
12939
  // path probes a folded copy of the input (offsets are unchanged, so the
12940
  // captures still slice the original). Inputs beyond the fast-path length
12941
  // are folded on the fly by the sequential matcher instead.
12942
  char folded[max_fast_path_pathname_length];
12943
  std::string_view probe = pathname;
12944
  bool fold_input = false;
12945
  if (compiled_.ignore_case) {
12946
    if (pathname.size() <= max_fast_path_pathname_length) {
12947
      detail::ascii_fold(pathname.data(),
12948
                         static_cast<uint32_t>(pathname.size()), folded);
12949
      probe = std::string_view(folded, pathname.size());
12950
    } else {
12951
      fold_input = true;
12952
    }
12953
  }
12954
  detail::engine_result best{};
12955
  std::vector<std::optional<std::string>> best_groups{};
12956
  detail::match_compiled(compiled_, probe.data(),
12957
                         static_cast<uint32_t>(probe.size()), best);
12958
  const uint32_t* aux = compiled_.section<uint32_t>(compiled_.aux_offset);
12959
  if (!best.within_fast_path) {
12960
    // The input exceeds a fast-path limit (length, segment count, or no
12961
    // leading '/'): the sequential fallback matches every route with
12962
    // identical priority semantics.
12963
    best = detail::engine_result{};
12964
    for (size_t i = 0; i < patterns_.size(); i++) {
12965
      consider_route(static_cast<uint32_t>(i), pathname, probe, fold_input,
12966
                     best, best_groups);
12967
    }
12968
  } else if (best.route >= 0) {
12969
    // A fast-path winner is final except for the auxiliary routes recorded
12970
    // at creation as able to outrank it (usually none).
12971
    const detail::route_record& winner =
12972
        compiled_.section<detail::route_record>(
12973
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12974
    for (uint32_t k = 0; k < winner.challenger_count; k++) {
12975
      consider_route(aux[winner.challenger_first + k], pathname, probe,
12976
                     fold_input, best, best_groups);
12977
    }
12978
  } else {
12979
    // A fast-path miss: any auxiliary route may still match.
12980
    for (uint32_t k = 0; k < compiled_.n_aux_all; k++) {
12981
      consider_route(aux[k], pathname, probe, fold_input, best, best_groups);
12982
    }
12983
  }
12984
  url_pattern_list_match_result result{};
12985
  result.route_index = best.route;
12986
  if (best.route >= 0) {
12987
    const detail::route_record& winner =
12988
        compiled_.section<detail::route_record>(
12989
            compiled_.routes_offset)[static_cast<size_t>(best.route)];
12990
    if (winner.mode == detail::route_mode::regexp) {
12991
      result.regexp_route = true;
12992
      result.regexp_groups = std::move(best_groups);
12993
    } else {
12994
      result.capture_count = best.capture_count;
12995
      result.captures_truncated = best.captures_truncated;
12996
      result.captures = best.captures;
12997
    }
12998
  }
12999
  return result;
13000
}
13001
13002
}  // namespace ada
13003
#endif  // ADA_INCLUDE_URL_PATTERN
13004
#endif  // ADA_URL_PATTERN_LIST_INL_H
13005
/* end file include/ada/url_pattern_list-inl.h */
13006
13007
// Public API
13008
/* begin file include/ada/ada_version.h */
13009
/**
13010
 * @file ada_version.h
13011
 * @brief Definitions for Ada's version number.
13012
 */
13013
#ifndef ADA_ADA_VERSION_H
13014
#define ADA_ADA_VERSION_H
13015
13016
0
#define ADA_VERSION "4.0.0"
13017
13018
namespace ada {
13019
13020
enum {
13021
  ADA_VERSION_MAJOR = 4,
13022
  ADA_VERSION_MINOR = 0,
13023
  ADA_VERSION_REVISION = 0,
13024
};
13025
13026
}  // namespace ada
13027
13028
#endif  // ADA_ADA_VERSION_H
13029
/* end file include/ada/ada_version.h */
13030
/* begin file include/ada/implementation-inl.h */
13031
/**
13032
 * @file implementation-inl.h
13033
 */
13034
#ifndef ADA_IMPLEMENTATION_INL_H
13035
#define ADA_IMPLEMENTATION_INL_H
13036
13037
13038
13039
#include <cstddef>
13040
#include <span>
13041
#include <string>
13042
#include <string_view>
13043
#include <utility>
13044
#include <variant>
13045
#include <vector>
13046
13047
namespace ada {
13048
13049
#if ADA_INCLUDE_URL_PATTERN
13050
template <url_pattern_regex::regex_concept regex_provider>
13051
ada_warn_unused tl::expected<url_pattern<regex_provider>, errors>
13052
parse_url_pattern(std::variant<std::string_view, url_pattern_init>&& input,
13053
                  const std::string_view* base_url,
13054
240k
                  const url_pattern_options* options) {
13055
240k
  return parser::parse_url_pattern_impl<regex_provider>(std::move(input),
13056
240k
                                                        base_url, options);
13057
240k
}
13058
13059
template <url_pattern_regex::regex_concept regex_provider>
13060
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13061
parse_url_pattern_list(std::span<const std::string_view> pathname_patterns,
13062
                       const std::string_view* base_url,
13063
                       const url_pattern_options* options) {
13064
  std::vector<std::string> processed;
13065
  processed.reserve(pathname_patterns.size());
13066
  for (const std::string_view pattern : pathname_patterns) {
13067
    if (base_url == nullptr) {
13068
      processed.emplace_back(pattern);
13069
      continue;
13070
    }
13071
    // With a base URL, each pattern is processed exactly as the pathname of
13072
    // a URLPatternInit would be: a relative pattern is resolved against the
13073
    // base URL's path (and an unparsable base URL is a type error).
13074
    url_pattern_init init{};
13075
    init.pathname = std::string(pattern);
13076
    init.base_url = std::string(*base_url);
13077
    auto result = url_pattern_init::process(
13078
        init, url_pattern_init::process_type::pattern);
13079
    if (!result) {
13080
      return tl::unexpected(result.error());
13081
    }
13082
    processed.push_back(std::move(result->pathname).value_or(std::string{}));
13083
  }
13084
  const bool ignore_case = options != nullptr && options->ignore_case;
13085
  auto list = url_pattern_list<regex_provider>::create(std::move(processed),
13086
                                                       ignore_case);
13087
  if (!list) {
13088
    return list;
13089
  }
13090
  // Routes outside the static/":param"/"*" subset are compiled as a
13091
  // URLPattern pathname component through the provider, with the same
13092
  // options parse_url_pattern would use (ignore_case included).
13093
  auto compile_options = url_pattern_compile_component_options::PATHNAME;
13094
  compile_options.ignore_case = ignore_case;
13095
  const url_pattern_list_detail::route_record* routes =
13096
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13097
          list->compiled_.routes_offset);
13098
  for (size_t i = 0; i < list->patterns_.size(); i++) {
13099
    if (routes[i].regexp_component < 0) {
13100
      continue;
13101
    }
13102
    auto component = url_pattern_component<regex_provider>::compile(
13103
        list->patterns_[i], url_pattern_helpers::canonicalize_pathname,
13104
        compile_options);
13105
    if (!component) {
13106
      return tl::unexpected(component.error());
13107
    }
13108
    list->compiled_.group_names[i] = component->group_name_list;
13109
    list->regexp_components_.push_back(std::move(*component));
13110
  }
13111
  return list;
13112
}
13113
13114
template <url_pattern_regex::regex_concept regex_provider>
13115
ada_warn_unused tl::expected<url_pattern_list<regex_provider>, errors>
13116
parse_url_pattern_list(std::span<const url_pattern<regex_provider>> patterns) {
13117
  std::vector<std::string> texts;
13118
  texts.reserve(patterns.size());
13119
  const bool ignore_case = !patterns.empty() && patterns[0].ignore_case();
13120
  for (const url_pattern<regex_provider>& pattern : patterns) {
13121
    if (pattern.ignore_case() != ignore_case) {
13122
      return tl::unexpected(errors::type_error);  // one flag per list
13123
    }
13124
    texts.emplace_back(pattern.get_pathname());
13125
  }
13126
  auto list =
13127
      url_pattern_list<regex_provider>::create(std::move(texts), ignore_case);
13128
  if (!list) {
13129
    return list;
13130
  }
13131
  // Routes outside the subset reuse the pattern's already compiled pathname
13132
  // component: no second create_instance through the provider.
13133
  const url_pattern_list_detail::route_record* routes =
13134
      list->compiled_.template section<url_pattern_list_detail::route_record>(
13135
          list->compiled_.routes_offset);
13136
  for (size_t i = 0; i < patterns.size(); i++) {
13137
    if (routes[i].regexp_component < 0) {
13138
      continue;
13139
    }
13140
    list->compiled_.group_names[i] =
13141
        patterns[i].pathname_component.group_name_list;
13142
    list->regexp_components_.push_back(patterns[i].pathname_component);
13143
  }
13144
  return list;
13145
}
13146
#endif  // ADA_INCLUDE_URL_PATTERN
13147
13148
}  // namespace ada
13149
13150
#endif  // ADA_IMPLEMENTATION_INL_H
13151
/* end file include/ada/implementation-inl.h */
13152
13153
#endif  // ADA_H
13154
/* end file include/ada.h */